fix #3704
[oweals/gnunet.git] / src / fs / fs_download.c
index 49c83bdc6e9c904c3821ea09730f0a6b226d2be7..79674336e970e20516e371eec33de08bdc3c29cf 100644 (file)
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet.
-     (C) 2001-2012 Christian Grothoff (and other contributing authors)
+     Copyright (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
@@ -393,10 +393,10 @@ check_completed (struct GNUNET_FS_DownloadContext *dc)
     GNUNET_FS_dequeue_ (dc->job_queue);
     dc->job_queue = NULL;
   }
-  if (GNUNET_SCHEDULER_NO_TASK != dc->task)
+  if (NULL != dc->task)
   {
     GNUNET_SCHEDULER_cancel (dc->task);
-    dc->task = GNUNET_SCHEDULER_NO_TASK;
+    dc->task = NULL;
   }
   if (NULL != dc->rfh)
   {
@@ -450,6 +450,18 @@ try_match_block (struct GNUNET_FS_DownloadContext *dc,
     return;
   if (dr->depth > 0)
   {
+    if ( (dc->offset > 0) ||
+         (dc->length < GNUNET_ntohll (dc->uri->data.chk.file_length)) )
+    {
+      /* NOTE: this test is not tight, but should suffice; the issue
+         here is that 'dr->num_children' may inherently only specify a
+         smaller range than what is in the original file;
+         thus, reconstruction of (some) inner blocks will fail.
+         FIXME: we might eventually want to write a tighter test to
+         maximize the circumstances under which we do succeed with
+         IBlock reconstruction. (need good tests though). */
+      return;
+    }
     complete = GNUNET_YES;
     for (i = 0; i < dr->num_children; i++)
     {
@@ -494,39 +506,43 @@ try_match_block (struct GNUNET_FS_DownloadContext *dc,
     }
     /* write block to disk */
     fn = (NULL != dc->filename) ? dc->filename : dc->temp_filename;
-    fh = GNUNET_DISK_file_open (fn,
-                                GNUNET_DISK_OPEN_READWRITE |
-                                GNUNET_DISK_OPEN_CREATE |
-                                GNUNET_DISK_OPEN_TRUNCATE,
-                                GNUNET_DISK_PERM_USER_READ |
-                                GNUNET_DISK_PERM_USER_WRITE |
-                                GNUNET_DISK_PERM_GROUP_READ |
-                                GNUNET_DISK_PERM_OTHER_READ);
-    if (NULL == fh)
+    if (NULL != fn)
     {
-      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", fn);
-      GNUNET_asprintf (&dc->emsg, _("Failed to open file `%s' for writing"),
-                       fn);
-      GNUNET_DISK_file_close (fh);
-      dr->state = BRS_ERROR;
-      pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
-      pi.value.download.specifics.error.message = dc->emsg;
-      GNUNET_FS_download_make_status_ (&pi, dc);
-      return;
-    }
-    if (data_len != GNUNET_DISK_file_write (fh, odata, odata_len))
-    {
-      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "write", fn);
-      GNUNET_asprintf (&dc->emsg, _("Failed to open file `%s' for writing"),
-                       fn);
+      fh = GNUNET_DISK_file_open (fn,
+                                  GNUNET_DISK_OPEN_READWRITE |
+                                  GNUNET_DISK_OPEN_CREATE |
+                                  GNUNET_DISK_OPEN_TRUNCATE,
+                                  GNUNET_DISK_PERM_USER_READ |
+                                  GNUNET_DISK_PERM_USER_WRITE |
+                                  GNUNET_DISK_PERM_GROUP_READ |
+                                  GNUNET_DISK_PERM_OTHER_READ);
+      if (NULL == fh)
+      {
+        GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", fn);
+        GNUNET_asprintf (&dc->emsg,
+                         _("Failed to open file `%s' for writing"),
+                         fn);
+        GNUNET_DISK_file_close (fh);
+        dr->state = BRS_ERROR;
+        pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
+        pi.value.download.specifics.error.message = dc->emsg;
+        GNUNET_FS_download_make_status_ (&pi, dc);
+        return;
+      }
+      if (data_len != GNUNET_DISK_file_write (fh, odata, odata_len))
+      {
+        GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "write", fn);
+        GNUNET_asprintf (&dc->emsg, _("Failed to open file `%s' for writing"),
+                         fn);
+        GNUNET_DISK_file_close (fh);
+        dr->state = BRS_ERROR;
+        pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
+        pi.value.download.specifics.error.message = dc->emsg;
+        GNUNET_FS_download_make_status_ (&pi, dc);
+        return;
+      }
       GNUNET_DISK_file_close (fh);
-      dr->state = BRS_ERROR;
-      pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
-      pi.value.download.specifics.error.message = dc->emsg;
-      GNUNET_FS_download_make_status_ (&pi, dc);
-      return;
     }
-    GNUNET_DISK_file_close (fh);
     /* signal success */
     dr->state = BRS_DOWNLOAD_UP;
     dc->completed = dc->length;
@@ -690,7 +706,7 @@ try_top_down_reconstruction (struct GNUNET_FS_DownloadContext *dc,
     drc = dr->children[i];
     GNUNET_assert (drc->offset >= dr->offset);
     child_block_size = GNUNET_FS_tree_compute_tree_size (drc->depth);
-    GNUNET_assert (0 == (drc->offset - dr->offset) % child_block_size);     
+    GNUNET_assert (0 == (drc->offset - dr->offset) % child_block_size);
     if (BRS_INIT == drc->state)
     {
       drc->state = BRS_CHK_SET;
@@ -934,7 +950,7 @@ GNUNET_FS_free_download_request_ (struct DownloadRequest *dr)
  * @param cls closure (our 'struct ProcessResultClosure')
  * @param key query for the given value / request
  * @param value value in the hash map (a 'struct DownloadRequest')
- * @return GNUNET_YES (we should continue to iterate); unless serious error
+ * @return #GNUNET_YES (we should continue to iterate); unless serious error
  */
 static int
 process_result_with_request (void *cls, const struct GNUNET_HashCode * key,
@@ -1083,7 +1099,7 @@ process_result_with_request (void *cls, const struct GNUNET_HashCode * key,
   pi.value.download.specifics.progress.respect_offered = prc->respect_offered;
   pi.value.download.specifics.progress.num_transmissions = prc->num_transmissions;
   if (prc->last_transmission.abs_value_us != GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
-    pi.value.download.specifics.progress.block_download_duration 
+    pi.value.download.specifics.progress.block_download_duration
       = GNUNET_TIME_absolute_get_duration (prc->last_transmission);
   else
     pi.value.download.specifics.progress.block_download_duration
@@ -1364,7 +1380,7 @@ do_reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   struct GNUNET_FS_DownloadContext *dc = cls;
   struct GNUNET_CLIENT_Connection *client;
 
-  dc->task = GNUNET_SCHEDULER_NO_TASK;
+  dc->task = NULL;
   client = GNUNET_CLIENT_connect ("fs", dc->h->cfg);
   if (NULL == client)
   {
@@ -1445,7 +1461,7 @@ try_reconnect (struct GNUNET_FS_DownloadContext *dc)
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will try to reconnect in %s\n",
              GNUNET_STRINGS_relative_time_to_string (dc->reconnect_backoff, GNUNET_YES));
   dc->task =
-    GNUNET_SCHEDULER_add_delayed (dc->reconnect_backoff, 
+    GNUNET_SCHEDULER_add_delayed (dc->reconnect_backoff,
                                  &do_reconnect,
                                  dc);
 }
@@ -1492,7 +1508,7 @@ activate_fs_download (void *cls, struct GNUNET_CLIENT_Connection *client)
 /**
  * We must stop to ask the FS service for our blocks.  Pause the download.
  *
- * @param cls the 'struct GNUNET_FS_DownloadContext'
+ * @param cls the `struct GNUNET_FS_DownloadContext`
  */
 static void
 deactivate_fs_download (void *cls)
@@ -1539,7 +1555,7 @@ deactivate_fs_download (void *cls)
  *         the specified depth
  */
 static struct DownloadRequest *
-create_download_request (struct DownloadRequest *parent, 
+create_download_request (struct DownloadRequest *parent,
                         unsigned int chk_idx,
                         unsigned int depth,
                          uint64_t dr_offset, uint64_t file_start_offset,
@@ -1550,7 +1566,7 @@ create_download_request (struct DownloadRequest *parent,
   unsigned int head_skip;
   uint64_t child_block_size;
 
-  dr = GNUNET_malloc (sizeof (struct DownloadRequest));
+  dr = GNUNET_new (struct DownloadRequest);
   dr->parent = parent;
   dr->depth = depth;
   dr->offset = dr_offset;
@@ -1558,7 +1574,7 @@ create_download_request (struct DownloadRequest *parent,
   if (0 == depth)
     return dr;
   child_block_size = GNUNET_FS_tree_compute_tree_size (depth - 1);
-  
+
   /* calculate how many blocks at this level are not interesting
    * from the start (rounded down), either because of the requested
    * file offset or because this IBlock is further along */
@@ -1570,7 +1586,7 @@ create_download_request (struct DownloadRequest *parent,
   {
     head_skip = 0;
   }
-  
+
   /* calculate index of last block at this level that is interesting (rounded up) */
   dr->num_children = (file_start_offset + desired_length - dr_offset) / child_block_size;
   if (dr->num_children * child_block_size <
@@ -1585,12 +1601,12 @@ create_download_request (struct DownloadRequest *parent,
              (unsigned long long) dr_offset,
              depth,
              dr->num_children);
-  
+
   /* now we can get the total number of *interesting* children for this block */
 
   /* why else would we have gotten here to begin with? (that'd be a bad logic error) */
   GNUNET_assert (dr->num_children > 0);
-  
+
   dr->children =
     GNUNET_malloc (dr->num_children * sizeof (struct DownloadRequest *));
   for (i = 0; i < dr->num_children; i++)
@@ -1616,11 +1632,11 @@ reconstruct_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct GNUNET_FS_DownloadContext *dc = cls;
 
-  /* clean up state from tree encoder */  
-  if (dc->task != GNUNET_SCHEDULER_NO_TASK)
+  /* clean up state from tree encoder */
+  if (dc->task != NULL)
   {
     GNUNET_SCHEDULER_cancel (dc->task);
-    dc->task = GNUNET_SCHEDULER_NO_TASK;
+    dc->task = NULL;
   }
   if (NULL != dc->rfh)
   {
@@ -1646,7 +1662,7 @@ get_next_block (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct GNUNET_FS_DownloadContext *dc = cls;
 
-  dc->task = GNUNET_SCHEDULER_NO_TASK;
+  dc->task = NULL;
   GNUNET_FS_tree_encoder_next (dc->te);
 }
 
@@ -1850,7 +1866,7 @@ GNUNET_FS_download_start_task_ (void *cls,
   struct GNUNET_DISK_FileHandle *fh;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start task running...\n");
-  dc->task = GNUNET_SCHEDULER_NO_TASK;
+  dc->task = NULL;
   if (0 == dc->length)
   {
     /* no bytes required! */
@@ -1965,7 +1981,7 @@ GNUNET_FS_download_start_task_ (void *cls,
     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_tree_encoder_create (dc->h,
                                     GNUNET_FS_uri_chk_get_file_size (dc->uri),
                                     dc, &fh_reader,
                                     &reconstruct_cb, NULL,
@@ -2012,10 +2028,10 @@ GNUNET_FS_download_signal_suspend_ (void *cls)
   if (NULL != dc->parent)
     GNUNET_CONTAINER_DLL_remove (dc->parent->child_head, dc->parent->child_tail,
                                  dc);
-  if (GNUNET_SCHEDULER_NO_TASK != dc->task)
+  if (NULL != dc->task)
   {
     GNUNET_SCHEDULER_cancel (dc->task);
-    dc->task = GNUNET_SCHEDULER_NO_TASK;
+    dc->task = NULL;
   }
   pi.status = GNUNET_FS_STATUS_DOWNLOAD_SUSPEND;
   GNUNET_FS_download_make_status_ (&pi, dc);
@@ -2081,7 +2097,7 @@ create_download_context (struct GNUNET_FS_Handle *h,
     GNUNET_break (0);
     return NULL;
   }
-  dc = GNUNET_malloc (sizeof (struct GNUNET_FS_DownloadContext));
+  dc = GNUNET_new (struct GNUNET_FS_DownloadContext);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Starting download %p, %u bytes at offset %llu\n",
              dc,
@@ -2116,7 +2132,7 @@ create_download_context (struct GNUNET_FS_Handle *h,
     else
       dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Starting download `%s' of %llu bytes with tree depth %u\n",
              filename,
              (unsigned long long) length,
@@ -2241,11 +2257,7 @@ GNUNET_FS_download_start_from_search (struct GNUNET_FS_Handle *h,
   {
     GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
     sr->probe_ctx = NULL;
-  }
-  if (GNUNET_SCHEDULER_NO_TASK != sr->probe_ping_task)
-  {
-    GNUNET_SCHEDULER_cancel (sr->probe_ping_task);
-    sr->probe_ping_task = GNUNET_SCHEDULER_NO_TASK;
+    GNUNET_FS_stop_probe_ping_task_ (sr);
   }
   return dc;
 }
@@ -2267,7 +2279,7 @@ GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc)
       GNUNET_FS_queue_ (dc->h, &activate_fs_download, &deactivate_fs_download,
                         dc, (dc->length + DBLOCK_SIZE - 1) / DBLOCK_SIZE,
                        (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
-                       ? GNUNET_FS_QUEUE_PRIORITY_NORMAL 
+                       ? GNUNET_FS_QUEUE_PRIORITY_NORMAL
                        : GNUNET_FS_QUEUE_PRIORITY_PROBE);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Download %p put into queue as job %p\n",
@@ -2275,6 +2287,39 @@ GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc)
              dc->job_queue);
 }
 
+/**
+ * Suspend a download.
+ *
+ * @param dc handle for the download
+ */
+void
+GNUNET_FS_download_suspend (struct GNUNET_FS_DownloadContext *dc)
+{
+       deactivate_fs_download(dc);
+}
+
+/**
+ * Resume a suspended download.
+ *
+ * @param dc handle for the download
+ */
+void
+GNUNET_FS_download_resume (struct GNUNET_FS_DownloadContext *dc)
+{
+    struct GNUNET_FS_ProgressInfo pi;
+
+    pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE;
+    GNUNET_FS_download_make_status_ (&pi, dc);
+
+    dc->job_queue =
+      GNUNET_FS_queue_ (dc->h, &activate_fs_download, &deactivate_fs_download,
+                        dc, (dc->length + DBLOCK_SIZE - 1) / DBLOCK_SIZE,
+                       (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
+                       ? GNUNET_FS_QUEUE_PRIORITY_NORMAL
+                       : GNUNET_FS_QUEUE_PRIORITY_PROBE);
+
+}
+
 
 /**
  * Stop a download (aborts if download is incomplete).
@@ -2291,10 +2336,10 @@ GNUNET_FS_download_stop (struct GNUNET_FS_DownloadContext *dc, int do_delete)
 
   if (NULL != dc->top)
     GNUNET_FS_end_top (dc->h, dc->top);
-  if (GNUNET_SCHEDULER_NO_TASK != dc->task)
+  if (NULL != dc->task)
   {
     GNUNET_SCHEDULER_cancel (dc->task);
-    dc->task = GNUNET_SCHEDULER_NO_TASK;
+    dc->task = NULL;
   }
   search_was_null = (NULL == dc->search);
   if (NULL != dc->search)