+ else
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Reconstruction failed, need to download block at offset %llu, depth %u\n",
+ (unsigned long long) offset,
+ depth);
+ break;
+ case BRS_DOWNLOAD_DOWN:
+ break;
+ case BRS_DOWNLOAD_UP:
+ break;
+ case BRS_ERROR:
+ break;
+ default:
+ GNUNET_assert (0);
+ break;
+ }
+ dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc);
+ if ((dr == dc->top_request) && (dr->state == BRS_DOWNLOAD_UP))
+ check_completed (dc);
+}
+
+
+/**
+ * Function called by the tree encoder to obtain a block of plaintext
+ * data (for the lowest level of the tree).
+ *
+ * @param cls our 'struct ReconstructContext'
+ * @param offset identifies which block to get
+ * @param max (maximum) number of bytes to get; returning
+ * fewer will also cause errors
+ * @param buf where to copy the plaintext buffer
+ * @param emsg location to store an error message (on error)
+ * @return number of bytes copied to buf, 0 on error
+ */
+static size_t
+fh_reader (void *cls, uint64_t offset, size_t max, void *buf, char **emsg)
+{
+ struct GNUNET_FS_DownloadContext *dc = cls;
+ struct GNUNET_DISK_FileHandle *fh = dc->rfh;
+ ssize_t ret;
+
+ if (NULL != emsg)
+ *emsg = NULL;
+ if (offset != GNUNET_DISK_file_seek (fh, offset, GNUNET_DISK_SEEK_SET))
+ {
+ if (NULL != emsg)
+ *emsg = GNUNET_strdup (strerror (errno));
+ return 0;
+ }
+ ret = GNUNET_DISK_file_read (fh, buf, max);
+ if (ret < 0)
+ {
+ if (NULL != emsg)
+ *emsg = GNUNET_strdup (strerror (errno));
+ return 0;
+ }
+ return ret;
+}
+
+
+/**
+ * Task that creates the initial (top-level) download
+ * request for the file.
+ *
+ * @param cls the 'struct GNUNET_FS_DownloadContext'
+ * @param tc scheduler context
+ */
+void
+GNUNET_FS_download_start_task_ (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_FS_DownloadContext *dc = cls;
+ struct GNUNET_FS_ProgressInfo pi;
+ struct GNUNET_DISK_FileHandle *fh;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start task running...\n");
+ dc->task = GNUNET_SCHEDULER_NO_TASK;
+ if (0 == dc->length)
+ {
+ /* no bytes required! */
+ if (NULL != dc->filename)
+ {
+ fh = GNUNET_DISK_file_open (dc->filename,
+ GNUNET_DISK_OPEN_READWRITE |
+ GNUNET_DISK_OPEN_CREATE |
+ ((0 ==
+ GNUNET_FS_uri_chk_get_file_size (dc->uri)) ?
+ GNUNET_DISK_OPEN_TRUNCATE : 0),
+ GNUNET_DISK_PERM_USER_READ |
+ GNUNET_DISK_PERM_USER_WRITE |
+ GNUNET_DISK_PERM_GROUP_READ |
+ GNUNET_DISK_PERM_OTHER_READ);
+ GNUNET_DISK_file_close (fh);
+ }
+ GNUNET_FS_download_sync_ (dc);
+ pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
+ pi.value.download.specifics.start.meta = dc->meta;
+ GNUNET_FS_download_make_status_ (&pi, dc);
+ check_completed (dc);
+ return;
+ }
+ if (NULL != dc->emsg)
+ return;
+ if (NULL == dc->top_request)
+ {
+ dc->top_request =
+ create_download_request (NULL, 0, dc->treedepth - 1, 0, dc->offset,
+ dc->length);
+ dc->top_request->state = BRS_CHK_SET;
+ dc->top_request->chk =
+ (dc->uri->type ==
+ chk) ? dc->uri->data.chk.chk : dc->uri->data.loc.fi.chk;
+ /* signal start */
+ GNUNET_FS_download_sync_ (dc);
+ if (NULL != dc->search)
+ GNUNET_FS_search_result_sync_ (dc->search);
+ pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
+ pi.value.download.specifics.start.meta = dc->meta;
+ GNUNET_FS_download_make_status_ (&pi, dc);
+ }
+ GNUNET_FS_download_start_downloading_ (dc);
+ /* attempt reconstruction from disk */
+ if (GNUNET_YES == GNUNET_DISK_file_test (dc->filename))
+ dc->rfh =
+ GNUNET_DISK_file_open (dc->filename, GNUNET_DISK_OPEN_READ,
+ GNUNET_DISK_PERM_NONE);
+ if (dc->top_request->state == BRS_CHK_SET)
+ {
+ if (NULL != dc->rfh)
+ {
+ /* first, try top-down */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Trying top-down reconstruction for `%s'\n", dc->filename);
+ try_top_down_reconstruction (dc, dc->top_request);
+ switch (dc->top_request->state)
+ {
+ case BRS_CHK_SET:
+ break; /* normal */
+ case BRS_DOWNLOAD_DOWN:
+ break; /* normal, some blocks already down */
+ case BRS_DOWNLOAD_UP:
+ /* already done entirely, party! */
+ if (NULL != dc->rfh)
+ {
+ /* avoid hanging on to file handle longer than
+ * necessary */
+ GNUNET_DISK_file_close (dc->rfh);
+ dc->rfh = NULL;
+ }
+ return;
+ case BRS_ERROR:
+ GNUNET_asprintf (&dc->emsg, _("Invalid URI"));
+ GNUNET_FS_download_sync_ (dc);
+ pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
+ pi.value.download.specifics.error.message = dc->emsg;
+ GNUNET_FS_download_make_status_ (&pi, dc);
+ return;
+ default:
+ GNUNET_assert (0);
+ break;
+ }
+ }
+ }
+ /* attempt reconstruction from meta data */
+ if ((GNUNET_FS_uri_chk_get_file_size (dc->uri) <= MAX_INLINE_SIZE) &&
+ (NULL != dc->meta))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Trying to find embedded meta data for download of size %llu with %u bytes MD\n",
+ (unsigned long long) GNUNET_FS_uri_chk_get_file_size (dc->uri),
+ (unsigned int)
+ GNUNET_CONTAINER_meta_data_get_serialized_size (dc->meta));
+ GNUNET_CONTAINER_meta_data_iterate (dc->meta, &match_full_data, dc);
+ if (BRS_DOWNLOAD_UP == dc->top_request->state)
+ {
+ if (NULL != dc->rfh)
+ {
+ /* avoid hanging on to file handle longer than
+ * necessary */
+ GNUNET_DISK_file_close (dc->rfh);
+ dc->rfh = NULL;
+ }
+ return; /* finished, status update was already done for us */
+ }
+ }
+ if (NULL != dc->rfh)
+ {
+ /* finally, actually run bottom-up */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Trying bottom-up reconstruction of file `%s'\n", dc->filename);
+ dc->te =
+ GNUNET_FS_tree_encoder_create (dc->h,
+ GNUNET_FS_uri_chk_get_file_size (dc->uri),
+ dc, &fh_reader,
+ &reconstruct_cb, NULL,
+ &reconstruct_cont);
+ dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc);
+ }
+ else
+ {
+ /* simple, top-level download */
+ dc->issue_requests = GNUNET_YES;
+ schedule_block_download (dc, dc->top_request);
+ }
+ if (BRS_DOWNLOAD_UP == dc->top_request->state)
+ check_completed (dc);
+}
+
+
+/**
+ * Create SUSPEND event for the given download operation
+ * and then clean up our state (without stop signal).
+ *
+ * @param cls the 'struct GNUNET_FS_DownloadContext' to signal for
+ */
+void
+GNUNET_FS_download_signal_suspend_ (void *cls)
+{
+ struct GNUNET_FS_DownloadContext *dc = cls;
+ struct GNUNET_FS_ProgressInfo pi;
+
+ if (NULL != dc->top)
+ GNUNET_FS_end_top (dc->h, dc->top);
+ while (NULL != dc->child_head)
+ GNUNET_FS_download_signal_suspend_ (dc->child_head);
+ if (NULL != dc->search)
+ {
+ dc->search->download = NULL;
+ dc->search = NULL;
+ }
+ if (NULL != dc->job_queue)
+ {
+ GNUNET_FS_dequeue_ (dc->job_queue);
+ dc->job_queue = NULL;
+ }
+ if (NULL != dc->parent)
+ GNUNET_CONTAINER_DLL_remove (dc->parent->child_head, dc->parent->child_tail,
+ dc);
+ if (GNUNET_SCHEDULER_NO_TASK != dc->task)
+ {
+ GNUNET_SCHEDULER_cancel (dc->task);
+ dc->task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ pi.status = GNUNET_FS_STATUS_DOWNLOAD_SUSPEND;
+ GNUNET_FS_download_make_status_ (&pi, dc);
+ if (NULL != dc->te)
+ {
+ GNUNET_FS_tree_encoder_finish (dc->te, NULL, NULL);
+ dc->te = NULL;
+ }
+ if (NULL != dc->rfh)
+ {
+ GNUNET_DISK_file_close (dc->rfh);
+ dc->rfh = NULL;
+ }
+ GNUNET_FS_free_download_request_ (dc->top_request);
+ if (NULL != dc->active)
+ {
+ GNUNET_CONTAINER_multihashmap_destroy (dc->active);
+ dc->active = NULL;
+ }
+ GNUNET_free_non_null (dc->filename);
+ GNUNET_CONTAINER_meta_data_destroy (dc->meta);
+ GNUNET_FS_uri_destroy (dc->uri);
+ GNUNET_free_non_null (dc->temp_filename);
+ GNUNET_free_non_null (dc->serialization);
+ GNUNET_free (dc);
+}
+
+
+/**
+ * Helper function to setup the download context.
+ *
+ * @param h handle to the file sharing subsystem
+ * @param uri the URI of the file (determines what to download); CHK or LOC URI
+ * @param meta known metadata for the file (can be NULL)
+ * @param filename where to store the file, maybe NULL (then no file is
+ * created on disk and data must be grabbed from the callbacks)
+ * @param tempname where to store temporary file data, not used if filename is non-NULL;
+ * can be NULL (in which case we will pick a name if needed); the temporary file
+ * may already exist, in which case we will try to use the data that is there and
+ * if it is not what is desired, will overwrite it
+ * @param offset at what offset should we start the download (typically 0)
+ * @param length how many bytes should be downloaded starting at offset
+ * @param anonymity anonymity level to use for the download
+ * @param options various options
+ * @param cctx initial value for the client context for this download
+ * @return context that can be used to control this download
+ */
+struct GNUNET_FS_DownloadContext *
+create_download_context (struct GNUNET_FS_Handle *h,
+ const struct GNUNET_FS_Uri *uri,
+ const struct GNUNET_CONTAINER_MetaData *meta,
+ const char *filename, const char *tempname,
+ uint64_t offset, uint64_t length, uint32_t anonymity,
+ enum GNUNET_FS_DownloadOptions options, void *cctx)
+{
+ struct GNUNET_FS_DownloadContext *dc;
+
+ GNUNET_assert (GNUNET_FS_uri_test_chk (uri) || GNUNET_FS_uri_test_loc (uri));
+ if ((offset + length < offset) ||
+ (offset + length > GNUNET_FS_uri_chk_get_file_size (uri)))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ dc = GNUNET_malloc (sizeof (struct GNUNET_FS_DownloadContext));
+ dc->h = h;
+ dc->uri = GNUNET_FS_uri_dup (uri);
+ dc->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
+ dc->client_info = cctx;
+ dc->start_time = GNUNET_TIME_absolute_get ();
+ if (NULL != filename)
+ {
+ dc->filename = GNUNET_strdup (filename);
+ if (GNUNET_YES == GNUNET_DISK_file_test (filename))
+ GNUNET_break (GNUNET_OK == GNUNET_DISK_file_size (filename, &dc->old_file_size, GNUNET_YES, GNUNET_YES));
+ }
+ if (GNUNET_FS_uri_test_loc (dc->uri))
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_FS_uri_loc_get_peer_identity (dc->uri, &dc->target));
+ dc->offset = offset;
+ dc->length = length;
+ dc->anonymity = anonymity;
+ dc->options = options;
+ dc->active =
+ GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE));
+ dc->treedepth =
+ GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size (dc->uri));
+ if ((NULL == filename) && (is_recursive_download (dc)))
+ {
+ if (NULL != tempname)
+ dc->temp_filename = GNUNET_strdup (tempname);
+ else
+ dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Starting download `%s' of %llu bytes with tree depth %u\n",
+ filename,
+ (unsigned long long) length,
+ dc->treedepth);
+ dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc);
+ return dc;