+ dr = dr->children[chld - dr->children[0]->chk_idx];
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Matched TE block with request at offset %llu and depth %u in state %d\n",
+ (unsigned long long) dr->offset,
+ dr->depth,
+ dr->state);
+ /* FIXME: this code needs more testing and might
+ need to handle more states... */
+ switch (dr->state)
+ {
+ case BRS_INIT:
+ break;
+ case BRS_RECONSTRUCT_DOWN:
+ break;
+ case BRS_RECONSTRUCT_META_UP:
+ break;
+ case BRS_RECONSTRUCT_UP:
+ break;
+ case BRS_CHK_SET:
+ if (0 == memcmp (chk, &dr->chk, sizeof (struct ContentHashKey)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Reconstruction succeeded, can use block at offset %llu, depth %u\n",
+ (unsigned long long) offset,
+ depth);
+ /* block matches, hence tree below matches;
+ * this request is done! */
+ dr->state = BRS_DOWNLOAD_UP;
+ (void) GNUNET_CONTAINER_multihashmap_remove (dc->active, &dr->chk.query, dr);
+ if (GNUNET_YES == dr->is_pending)
+ {
+ GNUNET_break (0); /* how did we get here? */
+ GNUNET_CONTAINER_DLL_remove (dc->pending_head, dc->pending_tail, dr);
+ dr->is_pending = GNUNET_NO;
+ }
+ /* calculate how many bytes of payload this block
+ * corresponds to */
+ blen = GNUNET_FS_tree_compute_tree_size (dr->depth);
+ /* how many of those bytes are in the requested range? */
+ blen = GNUNET_MIN (blen, dc->length + dc->offset - dr->offset);
+ /* signal progress */
+ dc->completed += blen;
+ pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
+ pi.value.download.specifics.progress.data = NULL;
+ pi.value.download.specifics.progress.offset = offset;
+ pi.value.download.specifics.progress.data_len = 0;
+ pi.value.download.specifics.progress.depth = 0;
+ pi.value.download.specifics.progress.trust_offered = 0;
+ pi.value.download.specifics.progress.block_download_duration = GNUNET_TIME_UNIT_ZERO;
+ GNUNET_FS_download_make_status_ (&pi, dc);
+ /* FIXME: duplicated code from 'process_result_with_request - refactor */
+ if (dc->completed == dc->length)
+ {
+ /* download completed, signal */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Download completed, truncating file to desired length %llu\n",
+ (unsigned long long) GNUNET_ntohll (dc->uri->data.
+ chk.file_length));
+ /* truncate file to size (since we store IBlocks at the end) */
+ if (NULL != dc->filename)
+ {
+ if (0 !=
+ truncate (dc->filename,
+ GNUNET_ntohll (dc->uri->data.chk.file_length)))
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "truncate",
+ dc->filename);
+ }
+ }
+ }
+ 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);