GNUnet is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
- by the Free Software Foundation; either version 2, or (at your
+ by the Free Software Foundation; either version 3, or (at your
option) any later version.
GNUnet is distributed in the hope that it will be useful, but
* @author Christian Grothoff
*
* TODO:
- * - location URI suppport (can wait, easy)
* - different priority for scheduling probe downloads?
- * - check if iblocks can be computed from existing blocks (can wait, hard)
*/
#include "platform.h"
#include "gnunet_constants.h"
= GNUNET_TIME_calculate_eta (dc->start_time,
dc->completed,
dc->length);
+ pi->value.download.is_active = (dc->client == NULL) ? GNUNET_NO : GNUNET_YES;
if (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
dc->client_info = dc->h->upcb (dc->h->upcb_cls,
pi);
void *value);
+/**
+ * We've found a matching block without downloading it.
+ * Encrypt it and pass it to our "receive" function as
+ * if we had received it from the network.
+ *
+ * @param dc download in question
+ * @param chk request this relates to
+ * @param sm request details
+ * @param block plaintext data matching request
+ * @param len number of bytes in block
+ * @param depth depth of the block
+ * @param do_store should we still store the block on disk?
+ * @return GNUNET_OK on success
+ */
+static int
+encrypt_existing_match (struct GNUNET_FS_DownloadContext *dc,
+ const struct ContentHashKey *chk,
+ struct DownloadRequest *sm,
+ const char * block,
+ size_t len,
+ int depth,
+ int do_store)
+{
+ struct ProcessResultClosure prc;
+ char enc[len];
+ struct GNUNET_CRYPTO_AesSessionKey sk;
+ struct GNUNET_CRYPTO_AesInitializationVector iv;
+ GNUNET_HashCode query;
+
+ GNUNET_CRYPTO_hash_to_aes_key (&chk->key, &sk, &iv);
+ if (-1 == GNUNET_CRYPTO_aes_encrypt (block, len,
+ &sk,
+ &iv,
+ enc))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_CRYPTO_hash (enc, len, &query);
+ if (0 != memcmp (&query,
+ &chk->query,
+ sizeof (GNUNET_HashCode)))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+#if DEBUG_DOWNLOAD
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Matching block already present, no need for download!\n");
+#endif
+ /* already got it! */
+ prc.dc = dc;
+ prc.data = enc;
+ prc.size = len;
+ prc.type = (dc->treedepth == depth)
+ ? GNUNET_BLOCK_TYPE_FS_DBLOCK
+ : GNUNET_BLOCK_TYPE_FS_IBLOCK;
+ prc.query = chk->query;
+ prc.do_store = do_store;
+ process_result_with_request (&prc,
+ &chk->key,
+ sm);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Closure for match_full_data.
+ */
+struct MatchDataContext
+{
+ /**
+ * CHK we are looking for.
+ */
+ const struct ContentHashKey *chk;
+
+ /**
+ * Download we're processing.
+ */
+ struct GNUNET_FS_DownloadContext *dc;
+
+ /**
+ * Request details.
+ */
+ struct DownloadRequest *sm;
+
+ /**
+ * Overall offset in the file.
+ */
+ uint64_t offset;
+
+ /**
+ * Desired length of the block.
+ */
+ size_t len;
+
+ /**
+ * Flag set to GNUNET_YES on success.
+ */
+ int done;
+};
+
+/**
+ * Type of a function that libextractor calls for each
+ * meta data item found.
+ *
+ * @param cls closure (user-defined)
+ * @param plugin_name name of the plugin that produced this value;
+ * special values can be used (i.e. '<zlib>' for zlib being
+ * used in the main libextractor library and yielding
+ * meta data).
+ * @param type libextractor-type describing the meta data
+ * @param format basic format information about data
+ * @param data_mime_type mime-type of data (not of the original file);
+ * can be NULL (if mime-type is not known)
+ * @param data actual meta-data found
+ * @param data_len number of bytes in data
+ * @return 0 to continue extracting, 1 to abort
+ */
+static int
+match_full_data (void *cls,
+ const char *plugin_name,
+ enum EXTRACTOR_MetaType type,
+ enum EXTRACTOR_MetaFormat format,
+ const char *data_mime_type,
+ const char *data,
+ size_t data_len)
+{
+ struct MatchDataContext *mdc = cls;
+ GNUNET_HashCode key;
+
+ if (type == EXTRACTOR_METATYPE_GNUNET_FULL_DATA)
+ {
+ if ( (mdc->offset > data_len) ||
+ (mdc->offset + mdc->len > data_len) )
+ return 1;
+ GNUNET_CRYPTO_hash (&data[mdc->offset],
+ mdc->len,
+ &key);
+ if (0 != memcmp (&key,
+ &mdc->chk->key,
+ sizeof (GNUNET_HashCode)))
+ {
+ GNUNET_break_op (0);
+ return 1;
+ }
+ /* match found! */
+ if (GNUNET_OK !=
+ encrypt_existing_match (mdc->dc,
+ mdc->chk,
+ mdc->sm,
+ &data[mdc->offset],
+ mdc->len,
+ 0,
+ GNUNET_YES))
+ {
+ GNUNET_break_op (0);
+ return 1;
+ }
+ mdc->done = GNUNET_YES;
+ return 1;
+ }
+ return 0;
+}
+
+
+
+/**
+ * Closure for 'reconstruct_cont' and 'reconstruct_cb'.
+ */
+struct ReconstructContext
+{
+ /**
+ * File handle open for the reconstruction.
+ */
+ struct GNUNET_DISK_FileHandle *fh;
+
+ /**
+ * the download context.
+ */
+ struct GNUNET_FS_DownloadContext *dc;
+
+ /**
+ * Tree encoder used for the reconstruction.
+ */
+ struct GNUNET_FS_TreeEncoder *te;
+
+ /**
+ * CHK of block we are trying to reconstruct.
+ */
+ struct ContentHashKey chk;
+
+ /**
+ * Request that was generated.
+ */
+ struct DownloadRequest *sm;
+
+ /**
+ * Helper task.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier task;
+
+ /**
+ * Offset of block we are trying to reconstruct.
+ */
+ uint64_t offset;
+
+ /**
+ * Depth of block we are trying to reconstruct.
+ */
+ unsigned int depth;
+
+};
+
+
+/**
+ * Continuation after a possible attempt to reconstruct
+ * the current IBlock from the existing file.
+ *
+ * @param cls the 'struct ReconstructContext'
+ * @param tc scheduler context
+ */
+static void
+reconstruct_cont (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct ReconstructContext *rcc = cls;
+
+ if (rcc->te != NULL)
+ {
+ GNUNET_FS_tree_encoder_finish (rcc->te, NULL, NULL);
+ }
+ rcc->dc->reconstruct_failed = GNUNET_YES;
+ rcc->dc->rcc = NULL;
+ if (rcc->fh != NULL)
+ GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (rcc->fh));
+ if ( (rcc->dc->th == NULL) &&
+ (rcc->dc->client != NULL) )
+ {
+#if DEBUG_DOWNLOAD
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Asking for transmission to FS service\n");
+#endif
+ rcc->dc->th = GNUNET_CLIENT_notify_transmit_ready (rcc->dc->client,
+ sizeof (struct SearchMessage),
+ GNUNET_CONSTANTS_SERVICE_TIMEOUT,
+ GNUNET_NO,
+ &transmit_download_request,
+ rcc->dc);
+ }
+ else
+ {
+#if DEBUG_DOWNLOAD
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Transmission request not issued (%p %p)\n",
+ rcc->dc->th,
+ rcc->dc->client);
+#endif
+ }
+ GNUNET_free (rcc);
+}
+
+
+static void
+get_next_block (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct ReconstructContext *rcc = cls;
+
+ rcc->task = GNUNET_SCHEDULER_NO_TASK;
+ GNUNET_FS_tree_encoder_next (rcc->te);
+}
+
+
+/**
+ * Function called asking for the current (encoded)
+ * block to be processed. After processing the
+ * client should either call "GNUNET_FS_tree_encode_next"
+ * or (on error) "GNUNET_FS_tree_encode_finish".
+ *
+ * This function checks if the content on disk matches
+ * the expected content based on the URI.
+ *
+ * @param cls closure
+ * @param query the query for the block (key for lookup in the datastore)
+ * @param offset offset of the block
+ * @param type type of the block (IBLOCK or DBLOCK)
+ * @param block the (encrypted) block
+ * @param block_size size of block (in bytes)
+ */
+static void
+reconstruct_cb (void *cls,
+ const GNUNET_HashCode *query,
+ uint64_t offset,
+ unsigned int depth,
+ enum GNUNET_BLOCK_Type type,
+ const void *block,
+ uint16_t block_size)
+{
+ struct ReconstructContext *rcc = cls;
+ struct ProcessResultClosure prc;
+ struct GNUNET_FS_TreeEncoder *te;
+ uint64_t off;
+ uint64_t boff;
+ uint64_t roff;
+ unsigned int i;
+
+ roff = offset / DBLOCK_SIZE;
+ for (i=rcc->dc->treedepth;i>depth;i--)
+ roff /= CHK_PER_INODE;
+ boff = roff * DBLOCK_SIZE;
+ for (i=rcc->dc->treedepth;i>depth;i--)
+ boff *= CHK_PER_INODE;
+ /* convert reading offset into IBLOCKs on-disk offset */
+ off = compute_disk_offset (GNUNET_FS_uri_chk_get_file_size (rcc->dc->uri),
+ boff,
+ depth,
+ rcc->dc->treedepth);
+ if ( (off == rcc->offset) &&
+ (depth == rcc->depth) &&
+ (0 == memcmp (query,
+ &rcc->chk.query,
+ sizeof (GNUNET_HashCode))) )
+ {
+ /* already got it! */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ _("Block reconstruction at offset %llu and depth %u successful\n"),
+ (unsigned long long) offset,
+ depth);
+ prc.dc = rcc->dc;
+ prc.data = block;
+ prc.size = block_size;
+ prc.type = type;
+ prc.query = rcc->chk.query;
+ prc.do_store = GNUNET_NO;
+ process_result_with_request (&prc,
+ &rcc->chk.key,
+ rcc->sm);
+ te = rcc->te;
+ rcc->te = NULL;
+ GNUNET_FS_tree_encoder_finish (te, NULL, NULL);
+ GNUNET_free (rcc);
+ return;
+ }
+ rcc->task = GNUNET_SCHEDULER_add_now (&get_next_block,
+ rcc);
+}
+
+
+/**
+ * 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 ReconstructContext *rcc = cls;
+ struct GNUNET_DISK_FileHandle *fh = rcc->fh;
+ ssize_t ret;
+
+ *emsg = NULL;
+ if (offset !=
+ GNUNET_DISK_file_seek (fh,
+ offset,
+ GNUNET_DISK_SEEK_SET))
+ {
+ *emsg = GNUNET_strdup (strerror (errno));
+ return 0;
+ }
+ ret = GNUNET_DISK_file_read (fh, buf, max);
+ if (ret < 0)
+ {
+ *emsg = GNUNET_strdup (strerror (errno));
+ return 0;
+ }
+ return ret;
+}
+
/**
* Schedule the download of the specified block in the tree.
size_t len;
char block[DBLOCK_SIZE];
GNUNET_HashCode key;
- struct ProcessResultClosure prc;
+ struct MatchDataContext mdc;
struct GNUNET_DISK_FileHandle *fh;
+ struct ReconstructContext *rcc;
-#if DEBUG_DOWNLOAD
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Scheduling download at offset %llu and depth %u for `%s'\n",
- (unsigned long long) offset,
- depth,
- GNUNET_h2s (&chk->query));
-#endif
- total = GNUNET_ntohll (dc->uri->data.chk.file_length);
- off = compute_disk_offset (total,
- offset,
- depth,
- dc->treedepth);
+ total = GNUNET_FS_uri_chk_get_file_size (dc->uri);
len = GNUNET_FS_tree_calculate_block_size (total,
dc->treedepth,
offset,
depth);
+ off = compute_disk_offset (total,
+ offset,
+ depth,
+ dc->treedepth);
sm = GNUNET_malloc (sizeof (struct DownloadRequest));
sm->chk = *chk;
sm->offset = offset;
&chk->query,
sm,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
- fh = NULL;
- if ( (dc->old_file_size > off) &&
- (dc->filename != NULL) )
- {
- fh = GNUNET_DISK_file_open (dc->filename,
- GNUNET_DISK_OPEN_READ,
- GNUNET_DISK_PERM_NONE);
+ if ( (dc->tried_full_data == GNUNET_NO) &&
+ (depth == 0) )
+ {
+ mdc.dc = dc;
+ mdc.sm = sm;
+ mdc.chk = chk;
+ mdc.offset = offset;
+ mdc.len = len;
+ mdc.done = GNUNET_NO;
+ GNUNET_CONTAINER_meta_data_iterate (dc->meta,
+ &match_full_data,
+ &mdc);
+ if (mdc.done == GNUNET_YES)
+ return;
+ dc->tried_full_data = GNUNET_YES;
}
+#if DEBUG_DOWNLOAD
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Scheduling download at offset %llu and depth %u for `%s'\n",
+ (unsigned long long) offset,
+ depth,
+ GNUNET_h2s (&chk->query));
+#endif
+ fh = NULL;
+ if ( ( (dc->old_file_size > off) ||
+ ( (depth < dc->treedepth) &&
+ (dc->reconstruct_failed == GNUNET_NO) ) ) &&
+ (dc->filename != NULL) )
+ fh = GNUNET_DISK_file_open (dc->filename,
+ GNUNET_DISK_OPEN_READ,
+ GNUNET_DISK_PERM_NONE);
if ( (fh != NULL) &&
+ (dc->old_file_size > off) &&
(off ==
GNUNET_DISK_file_seek (fh,
off,
len)) )
{
GNUNET_CRYPTO_hash (block, len, &key);
- if (0 == memcmp (&key,
- &chk->key,
- sizeof (GNUNET_HashCode)))
+ if ( (0 == memcmp (&key,
+ &chk->key,
+ sizeof (GNUNET_HashCode))) &&
+ (GNUNET_OK ==
+ encrypt_existing_match (dc,
+ chk,
+ sm,
+ block,
+ len,
+ depth,
+ GNUNET_NO)) )
{
- char enc[len];
- struct GNUNET_CRYPTO_AesSessionKey sk;
- struct GNUNET_CRYPTO_AesInitializationVector iv;
- GNUNET_HashCode query;
-
- GNUNET_CRYPTO_hash_to_aes_key (&key, &sk, &iv);
- GNUNET_CRYPTO_aes_encrypt (block, len,
- &sk,
- &iv,
- enc);
- GNUNET_CRYPTO_hash (enc, len, &query);
- if (0 == memcmp (&query,
- &chk->query,
- sizeof (GNUNET_HashCode)))
- {
- /* already got it! */
- prc.dc = dc;
- prc.data = enc;
- prc.size = len;
- prc.type = (dc->treedepth == depth)
- ? GNUNET_BLOCK_TYPE_DBLOCK
- : GNUNET_BLOCK_TYPE_IBLOCK;
- prc.query = chk->query;
- prc.do_store = GNUNET_NO; /* useless */
- process_result_with_request (&prc,
- &key,
- sm);
- }
- else
- {
- GNUNET_break_op (0);
- }
+ GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fh));
return;
}
}
- if (fh != NULL)
- GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fh));
- if (depth < dc->treedepth)
+ rcc = GNUNET_malloc (sizeof (struct ReconstructContext));
+ rcc->fh = fh;
+ rcc->dc = dc;
+ rcc->sm = sm;
+ rcc->chk = *chk;
+ rcc->offset = off;
+ rcc->depth = depth;
+ dc->rcc = rcc;
+ if ( (depth < dc->treedepth) &&
+ (dc->reconstruct_failed == GNUNET_NO) &&
+ (fh != NULL) )
{
- // FIXME: try if we could
- // reconstitute this IBLOCK
- // from the existing blocks on disk (can wait)
- // (read block(s), encode, compare with
- // query; if matches, simply return)
+ rcc->te = GNUNET_FS_tree_encoder_create (dc->h,
+ dc->old_file_size,
+ rcc,
+ fh_reader,
+ &reconstruct_cb,
+ NULL,
+ &reconstruct_cont);
+ GNUNET_FS_tree_encoder_next (rcc->te);
+ return;
}
-
- if ( (dc->th == NULL) &&
- (dc->client != NULL) )
- dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
- sizeof (struct SearchMessage),
- GNUNET_CONSTANTS_SERVICE_TIMEOUT,
- GNUNET_NO,
- &transmit_download_request,
- dc);
+ reconstruct_cont (rcc, NULL);
}
-
/**
* Suggest a filename based on given metadata.
*
}
+#define GNUNET_FS_URI_CHK_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_CHK_INFIX
+
/**
* We found an entry in a directory. Check if the respective child
* already exists and if not create the respective child download.
char *us;
char *ext;
char *dn;
+ char *pos;
char *full_name;
if (NULL == uri)
fn = NULL;
if (NULL == filename)
{
- fn = GNUNET_FS_meta_data_suggest_filename (meta);
+ fn = GNUNET_FS_meta_data_suggest_filename (meta);
if (fn == NULL)
{
us = GNUNET_FS_uri_to_string (uri);
- fn = GNUNET_strdup (&us [strlen (GNUNET_FS_URI_PREFIX
- GNUNET_FS_URI_CHK_INFIX)]);
+ fn = GNUNET_strdup (&us [strlen (GNUNET_FS_URI_CHK_PREFIX)]);
GNUNET_free (us);
}
else if (fn[0] == '.')
us = GNUNET_FS_uri_to_string (uri);
GNUNET_asprintf (&fn,
"%s%s",
- &us[strlen (GNUNET_FS_URI_PREFIX
- GNUNET_FS_URI_CHK_INFIX)], ext);
+ &us[strlen (GNUNET_FS_URI_CHK_PREFIX)], ext);
GNUNET_free (ext);
GNUNET_free (us);
}
+ /* change '\' to '/' (this should have happened
+ during insertion, but malicious peers may
+ not have done this) */
+ while (NULL != (pos = strstr (fn, "\\")))
+ *pos = '/';
+ /* remove '../' everywhere (again, well-behaved
+ peers don't do this, but don't trust that
+ we did not get something nasty) */
+ while (NULL != (pos = strstr (fn, "../")))
+ {
+ pos[0] = '_';
+ pos[1] = '_';
+ pos[2] = '_';
+ }
filename = fn;
}
if (dc->filename == NULL)
}
+/**
+ * Free entries in the map.
+ *
+ * @param cls unused (NULL)
+ * @param key unused
+ * @param entry entry of type "struct DownloadRequest" which is freed
+ * @return GNUNET_OK
+ */
+static int
+free_entry (void *cls,
+ const GNUNET_HashCode *key,
+ void *entry)
+{
+ GNUNET_free (entry);
+ return GNUNET_OK;
+}
+
+
/**
* Iterator over entries in the pending requests in the 'active' map for the
* reply that we just got.
ppos = ppos->next;
}
GNUNET_CRYPTO_hash_to_aes_key (&sm->chk.key, &skey, &iv);
- GNUNET_CRYPTO_aes_decrypt (prc->data,
- prc->size,
- &skey,
- &iv,
- pt);
+ if (-1 == GNUNET_CRYPTO_aes_decrypt (prc->data,
+ prc->size,
+ &skey,
+ &iv,
+ pt))
+ {
+ GNUNET_break (0);
+ dc->emsg = GNUNET_strdup ("internal error decrypting content");
+ goto signal_error;
+ }
off = compute_disk_offset (GNUNET_ntohll (dc->uri->data.chk.file_length),
sm->offset,
sm->depth,
dc->th = NULL;
}
GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
- /* FIXME: clean up dc->active / pending! */
+ GNUNET_CONTAINER_multihashmap_iterate (dc->active,
+ &free_entry,
+ NULL);
+ dc->pending = NULL;
dc->client = NULL;
GNUNET_free (sm);
GNUNET_FS_download_sync_ (dc);
dc->th = NULL;
if (NULL == buf)
{
+#if DEBUG_DOWNLOAD
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Transmitting download request failed, trying to reconnect\n");
+#endif
try_reconnect (dc);
return 0;
}
msize = 0;
sm = buf;
while ( (dc->pending != NULL) &&
- (size > msize + sizeof (struct SearchMessage)) )
+ (size >= msize + sizeof (struct SearchMessage)) )
{
#if DEBUG_DOWNLOAD
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
else
sm->options = htonl (0);
if (dc->pending->depth == dc->treedepth)
- sm->type = htonl (GNUNET_BLOCK_TYPE_DBLOCK);
+ sm->type = htonl (GNUNET_BLOCK_TYPE_FS_DBLOCK);
else
- sm->type = htonl (GNUNET_BLOCK_TYPE_IBLOCK);
+ sm->type = htonl (GNUNET_BLOCK_TYPE_FS_IBLOCK);
sm->anonymity_level = htonl (dc->anonymity);
sm->target = dc->target.hashPubKey;
sm->query = dc->pending->chk.query;
struct GNUNET_CLIENT_Connection *client;
dc->task = GNUNET_SCHEDULER_NO_TASK;
- client = GNUNET_CLIENT_connect (dc->h->sched,
- "fs",
+ client = GNUNET_CLIENT_connect ("fs",
dc->h->cfg);
if (NULL == client)
{
if (NULL != dc->client)
{
+#if DEBUG_DOWNLOAD
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Moving all requests back to pending list\n");
+#endif
if (NULL != dc->th)
{
GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
dc->client = NULL;
}
+#if DEBUG_DOWNLOAD
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Will try to reconnect in 1s\n");
+#endif
dc->task
- = GNUNET_SCHEDULER_add_delayed (dc->h->sched,
- GNUNET_TIME_UNIT_SECONDS,
+ = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
&do_reconnect,
dc);
}
struct GNUNET_FS_DownloadContext *dc = cls;
struct GNUNET_FS_ProgressInfo pi;
+#if DEBUG_DOWNLOAD
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Download activated\n");
+#endif
GNUNET_assert (NULL != client);
+ GNUNET_assert (dc->client == NULL);
+ GNUNET_assert (dc->th == NULL);
dc->client = client;
GNUNET_CLIENT_receive (client,
&receive_results,
GNUNET_CONTAINER_multihashmap_iterate (dc->active,
&retry_entry,
dc);
- if ( (dc->th == NULL) &&
- (dc->client != NULL) )
- dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
- sizeof (struct SearchMessage),
- GNUNET_CONSTANTS_SERVICE_TIMEOUT,
- GNUNET_NO,
- &transmit_download_request,
- dc);
+#if DEBUG_DOWNLOAD
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Asking for transmission to FS service\n");
+#endif
+ dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
+ sizeof (struct SearchMessage),
+ GNUNET_CONSTANTS_SERVICE_TIMEOUT,
+ GNUNET_NO,
+ &transmit_download_request,
+ dc);
+ GNUNET_assert (dc->th != NULL);
}
{
struct GNUNET_FS_DownloadContext *dc = cls;
struct GNUNET_FS_ProgressInfo pi;
-
+
+#if DEBUG_DOWNLOAD
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Download deactivated\n");
+#endif
if (NULL != dc->th)
{
GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
/**
- * Free entries in the map.
+ * Task that creates the initial (top-level) download
+ * request for the file.
*
- * @param cls unused (NULL)
- * @param key unused
- * @param entry entry of type "struct DownloadRequest" which is freed
- * @return GNUNET_OK
+ * @param cls the 'struct GNUNET_FS_DownloadContext'
+ * @param tc scheduler context
*/
-static int
-free_entry (void *cls,
- const GNUNET_HashCode *key,
- void *entry)
+void
+GNUNET_FS_download_start_task_ (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
{
- GNUNET_free (entry);
- return GNUNET_OK;
+ struct GNUNET_FS_DownloadContext *dc = cls;
+ struct GNUNET_FS_ProgressInfo pi;
+ struct GNUNET_DISK_FileHandle *fh;
+
+ dc->start_task = GNUNET_SCHEDULER_NO_TASK;
+ if (dc->length == 0)
+ {
+ /* no bytes required! */
+ if (dc->filename != NULL)
+ {
+ fh = GNUNET_DISK_file_open (dc->filename != NULL
+ ? dc->filename
+ : dc->temp_filename,
+ GNUNET_DISK_OPEN_READWRITE |
+ GNUNET_DISK_OPEN_CREATE,
+ 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);
+ }
+
+ pi.status = GNUNET_FS_STATUS_DOWNLOAD_COMPLETED;
+ GNUNET_FS_download_make_status_ (&pi, dc);
+ GNUNET_FS_download_sync_ (dc);
+ if (dc->parent != NULL)
+ check_completed (dc->parent);
+ return;
+ }
+ schedule_block_download (dc,
+ (dc->uri->type == chk)
+ ? &dc->uri->data.chk.chk
+ : &dc->uri->data.loc.fi.chk,
+ 0,
+ 1 /* 0 == CHK, 1 == top */);
+ GNUNET_FS_download_sync_ (dc);
+ GNUNET_FS_download_start_downloading_ (dc);
+ pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
+ pi.value.download.specifics.start.meta = dc->meta;
+ GNUNET_FS_download_make_status_ (&pi, dc);
}
{
struct GNUNET_FS_DownloadContext *dc = cls;
struct GNUNET_FS_ProgressInfo pi;
-
+
if (dc->top != NULL)
GNUNET_FS_end_top (dc->h, dc->top);
while (NULL != dc->child_head)
GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
dc->parent->child_tail,
dc);
- pi.status = GNUNET_FS_STATUS_DOWNLOAD_SUSPEND;
- GNUNET_FS_download_make_status_ (&pi, dc);
if (GNUNET_SCHEDULER_NO_TASK != dc->task)
- GNUNET_SCHEDULER_cancel (dc->h->sched,
- dc->task);
+ GNUNET_SCHEDULER_cancel (dc->task);
+ if (dc->start_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (dc->start_task);
+ dc->start_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ else
+ {
+ pi.status = GNUNET_FS_STATUS_DOWNLOAD_SUSPEND;
+ GNUNET_FS_download_make_status_ (&pi, dc);
+ }
+ if (dc->rcc != NULL)
+ {
+ if (dc->rcc->task != GNUNET_SCHEDULER_NO_TASK)
+ GNUNET_SCHEDULER_cancel (dc->rcc->task);
+ if (dc->rcc->te != NULL)
+ GNUNET_FS_tree_encoder_finish (dc->rcc->te, NULL, NULL);
+ dc->rcc = NULL;
+ }
GNUNET_CONTAINER_multihashmap_iterate (dc->active,
&free_entry,
NULL);
void *cctx,
struct GNUNET_FS_DownloadContext *parent)
{
- struct GNUNET_FS_ProgressInfo pi;
struct GNUNET_FS_DownloadContext *dc;
- GNUNET_assert (GNUNET_FS_uri_test_chk (uri));
+ GNUNET_assert (GNUNET_FS_uri_test_chk (uri) ||
+ GNUNET_FS_uri_test_loc (uri) );
+
if ( (offset + length < offset) ||
- (offset + length > uri->data.chk.file_length) )
+ (offset + length > GNUNET_FS_uri_chk_get_file_size (uri)) )
{
GNUNET_break (0);
return NULL;
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));
+ dc->treedepth = GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size(dc->uri));
if ( (filename == NULL) &&
(is_recursive_download (dc) ) )
{
"Download tree has depth %u\n",
dc->treedepth);
#endif
- pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
- pi.value.download.specifics.start.meta = meta;
- GNUNET_FS_download_make_status_ (&pi, dc);
- schedule_block_download (dc,
- &dc->uri->data.chk.chk,
- 0,
- 1 /* 0 == CHK, 1 == top */);
- GNUNET_FS_download_sync_ (dc);
- GNUNET_FS_download_start_downloading_ (dc);
if (parent == NULL)
- dc->top = GNUNET_FS_make_top (dc->h,
- &GNUNET_FS_download_signal_suspend_,
- dc);
-
+ {
+ dc->top = GNUNET_FS_make_top (dc->h,
+ &GNUNET_FS_download_signal_suspend_,
+ dc);
+ }
+ dc->start_task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc);
return dc;
}
enum GNUNET_FS_DownloadOptions options,
void *cctx)
{
- struct GNUNET_FS_ProgressInfo pi;
struct GNUNET_FS_DownloadContext *dc;
- if (sr->download != NULL)
+ if ( (sr == NULL) ||
+ (sr->download != NULL) )
{
GNUNET_break (0);
return NULL;
}
- GNUNET_assert (GNUNET_FS_uri_test_chk (sr->uri));
+ 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) )
{
"Download tree has depth %u\n",
dc->treedepth);
#endif
- pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
- pi.value.download.specifics.start.meta = dc->meta;
- GNUNET_FS_download_make_status_ (&pi, dc);
- schedule_block_download (dc,
- &dc->uri->data.chk.chk,
- 0,
- 1 /* 0 == CHK, 1 == top */);
- GNUNET_FS_download_sync_ (dc);
- GNUNET_FS_download_start_downloading_ (dc);
+ dc->start_task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc);
return dc;
}
-
/**
* Start the downloading process (by entering the queue).
*
void
GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc)
{
+ if (dc->completed == dc->length)
+ return;
+ GNUNET_assert (dc->job_queue == NULL);
dc->job_queue = GNUNET_FS_queue_ (dc->h,
&activate_fs_download,
&deactivate_fs_download,
if (dc->top != NULL)
GNUNET_FS_end_top (dc->h, dc->top);
+ if (dc->start_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (dc->start_task);
+ dc->start_task = GNUNET_SCHEDULER_NO_TASK;
+ }
if (dc->search != NULL)
{
dc->search->download = NULL;
GNUNET_FS_dequeue_ (dc->job_queue);
dc->job_queue = NULL;
}
+ if (dc->rcc != NULL)
+ {
+ if (dc->rcc->task != GNUNET_SCHEDULER_NO_TASK)
+ GNUNET_SCHEDULER_cancel (dc->rcc->task);
+ if (dc->rcc->te != NULL)
+ GNUNET_FS_tree_encoder_finish (dc->rcc->te, NULL, NULL);
+ dc->rcc = NULL;
+ }
have_children = (NULL != dc->child_head) ? GNUNET_YES : GNUNET_NO;
while (NULL != dc->child_head)
GNUNET_FS_download_stop (dc->child_head,
? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD
: GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD ,
dc->serialization);
- if (GNUNET_YES == have_children)
- GNUNET_FS_remove_sync_dir_ (dc->h,
- GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD,
+ if ( (GNUNET_YES == have_children) &&
+ (dc->parent == NULL) )
+ GNUNET_FS_remove_sync_dir_ (dc->h,
+ (dc->search != NULL)
+ ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD
+ : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
dc->serialization);
pi.status = GNUNET_FS_STATUS_DOWNLOAD_STOPPED;
GNUNET_FS_download_make_status_ (&pi, dc);
if (GNUNET_SCHEDULER_NO_TASK != dc->task)
- GNUNET_SCHEDULER_cancel (dc->h->sched,
- dc->task);
+ GNUNET_SCHEDULER_cancel (dc->task);
GNUNET_CONTAINER_multihashmap_iterate (dc->active,
&free_entry,
NULL);