/*
This file is part of GNUnet.
- (C) 2001-2011 Christian Grothoff (and other contributing authors)
+ (C) 2001-2012 Christian Grothoff (and other contributing authors)
GNUnet is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
* @file fs/fs_download.c
* @brief download methods
* @author Christian Grothoff
- *
- * TODO:
- * - different priority for scheduling probe downloads?
*/
#include "platform.h"
#include "gnunet_constants.h"
if (dr_offset < file_start_offset)
head_skip = file_start_offset / child_block_size;
else
- head_skip = dr_offset / child_block_size;
+ head_skip = 0;
/* calculate index of last block at this level that is interesting (rounded up) */
- dr->num_children = (file_start_offset + desired_length) / child_block_size;
+ dr->num_children = (file_start_offset + desired_length - dr_offset) / child_block_size;
if (dr->num_children * child_block_size <
- file_start_offset + desired_length)
+ file_start_offset + desired_length - dr_offset)
dr->num_children++; /* round up */
+ if (dr->num_children > CHK_PER_INODE)
+ dr->num_children = CHK_PER_INODE; /* cap at max */
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Block at offset %llu and depth %u has %u children\n",
(unsigned long long) dr_offset,
depth,
dr->num_children);
- /* now we can get the total number of children for this block */
+ /* now we can get the total number of *interesting* children for this block */
dr->num_children -= head_skip;
- if (dr->num_children > CHK_PER_INODE)
- dr->num_children = CHK_PER_INODE; /* cap at max */
/* why else would we have gotten here to begin with? (that'd be a bad logic error) */
GNUNET_assert (dr->num_children > 0);
struct GNUNET_DISK_FileHandle *fh = dc->rfh;
ssize_t ret;
- *emsg = NULL;
+ if (NULL != emsg)
+ *emsg = NULL;
if (offset != GNUNET_DISK_file_seek (fh, offset, GNUNET_DISK_SEEK_SET))
{
- *emsg = GNUNET_strdup (strerror (errno));
+ if (NULL != emsg)
+ *emsg = GNUNET_strdup (strerror (errno));
return 0;
}
ret = GNUNET_DISK_file_read (fh, buf, max);
if (ret < 0)
{
- *emsg = GNUNET_strdup (strerror (errno));
+ if (NULL != emsg)
+ *emsg = GNUNET_strdup (strerror (errno));
return 0;
}
return ret;
/**
- * Download parts of a file. Note that this will store
- * the blocks at the respective offset in the given file. Also, the
- * download is still using the blocking of the underlying FS
- * encoding. As a result, the download may *write* outside of the
- * given boundaries (if offset and length do not match the 32k FS
- * block boundaries). <p>
- *
- * This function should be used to focus a download towards a
- * particular portion of the file (optimization), not to strictly
- * limit the download to exactly those bytes.
+ * 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 anonymity anonymity level to use for the download
* @param options various options
* @param cctx initial value for the client context for this download
- * @param parent parent download to associate this download with (use NULL
- * for top-level downloads; useful for manually-triggered recursive downloads)
* @return context that can be used to control this download
*/
struct GNUNET_FS_DownloadContext *
-GNUNET_FS_download_start (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 *parent)
+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;
}
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting download `%s' of %llu bytes\n",
- filename, (unsigned long long) length);
dc = GNUNET_malloc (sizeof (struct GNUNET_FS_DownloadContext));
dc->h = h;
- dc->parent = parent;
- if (parent != NULL)
- {
- GNUNET_CONTAINER_DLL_insert (parent->child_head, parent->child_tail, dc);
- }
dc->uri = GNUNET_FS_uri_dup (uri);
dc->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
dc->client_info = cctx;
{
dc->filename = GNUNET_strdup (filename);
if (GNUNET_YES == GNUNET_DISK_file_test (filename))
- GNUNET_DISK_file_size (filename, &dc->old_file_size, GNUNET_YES);
+ 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_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE));
dc->treedepth =
GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size (dc->uri));
- if ((filename == NULL) && (is_recursive_download (dc)))
+ if ((NULL == filename) && (is_recursive_download (dc)))
{
- if (tempname != NULL)
+ 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, "Download tree has depth %u\n",
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Starting download `%s' of %llu bytes with tree depth %u\n",
+ filename,
+ (unsigned long long) length,
dc->treedepth);
- if (parent == NULL)
- {
+ dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc);
+ return dc;
+}
+
+
+/**
+ * Download parts of a file. Note that this will store
+ * the blocks at the respective offset in the given file. Also, the
+ * download is still using the blocking of the underlying FS
+ * encoding. As a result, the download may *write* outside of the
+ * given boundaries (if offset and length do not match the 32k FS
+ * block boundaries). <p>
+ *
+ * This function should be used to focus a download towards a
+ * particular portion of the file (optimization), not to strictly
+ * limit the download to exactly those bytes.
+ *
+ * @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
+ * @param parent parent download to associate this download with (use NULL
+ * for top-level downloads; useful for manually-triggered recursive downloads)
+ * @return context that can be used to control this download
+ */
+struct GNUNET_FS_DownloadContext *
+GNUNET_FS_download_start (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 *parent)
+{
+ struct GNUNET_FS_DownloadContext *dc;
+
+ dc = create_download_context (h, uri, meta, filename, tempname,
+ offset, length, anonymity, options, cctx);
+ if (NULL == dc)
+ return NULL;
+ dc->parent = parent;
+ if (NULL != parent)
+ GNUNET_CONTAINER_DLL_insert (parent->child_head, parent->child_tail, dc);
+ else
dc->top =
GNUNET_FS_make_top (dc->h, &GNUNET_FS_download_signal_suspend_, dc);
- }
- dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc);
return dc;
}
{
struct GNUNET_FS_DownloadContext *dc;
- if ((sr == NULL) || (sr->download != NULL))
+ if ((NULL == sr) || (NULL != sr->download))
{
GNUNET_break (0);
return NULL;
}
- GNUNET_assert (GNUNET_FS_uri_test_chk (sr->uri) ||
- GNUNET_FS_uri_test_loc (sr->uri));
- if ((offset + length < offset) ||
- (offset + length > sr->uri->data.chk.file_length))
- {
- GNUNET_break (0);
+ dc = create_download_context (h, sr->uri, sr->meta, filename, tempname,
+ offset, length, anonymity, options, cctx);
+ if (NULL == dc)
return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting download `%s' of %llu bytes\n",
- filename, (unsigned long long) length);
- dc = GNUNET_malloc (sizeof (struct GNUNET_FS_DownloadContext));
- dc->h = h;
dc->search = sr;
sr->download = dc;
- if (sr->probe_ctx != NULL)
+ if (NULL != sr->probe_ctx)
{
GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
sr->probe_ctx = NULL;
}
- dc->uri = GNUNET_FS_uri_dup (sr->uri);
- dc->meta = GNUNET_CONTAINER_meta_data_duplicate (sr->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_DISK_file_size (filename, &dc->old_file_size, 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_ntohll (dc->uri->data.chk.file_length));
- if ((filename == NULL) && (is_recursive_download (dc)))
- {
- if (tempname != NULL)
- dc->temp_filename = GNUNET_strdup (tempname);
- else
- dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");
- }
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download tree has depth %u\n",
- dc->treedepth);
- dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc);
return dc;
}