arg
[oweals/gnunet.git] / src / fs / fs_download.c
index 8d2e1b7a4326b05cf495246d7d8a6a68d77a5146..1565f9a3d5e2a6a5b97a7e57768fdb8286001956 100644 (file)
@@ -97,38 +97,6 @@ compute_disk_offset (uint64_t fsize,
 }
 
 
-/**
- * Given a block at the given offset and depth, calculate the offset
- * for the CHK at the given index.
- *
- * @param offset the offset of the first
- *        DBLOCK in the subtree of the 
- *        identified IBLOCK
- * @param depth the depth of the IBLOCK in the tree, 0 for DBLOCK
- * @param k which CHK in the IBLOCK are we 
- *        talking about
- * @return offset if k=0, otherwise an appropriately
- *         larger value (i.e., if depth = 1,
- *         the returned value should be offset+k*DBLOCK_SIZE)
- */
-static uint64_t
-compute_dblock_offset (uint64_t offset,
-                      unsigned int depth,
-                      unsigned int k)
-{
-  unsigned int i;
-  uint64_t lsize; /* what is the size of the sum of all DBlocks 
-                    that a CHK at depth i corresponds to? */
-
-  if (depth == 0)
-    return offset;
-  lsize = DBLOCK_SIZE;
-  for (i=1;i<depth;i++)
-    lsize *= CHK_PER_INODE;
-  return offset + k * lsize;
-}
-
-
 /**
  * Fill in all of the generic fields for a download event and call the
  * callback.
@@ -290,7 +258,9 @@ encrypt_existing_match (struct GNUNET_FS_DownloadContext *dc,
     }
 #if DEBUG_DOWNLOAD
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Matching block already present, no need for download!\n");
+             "Matching block for `%s' at offset %llu already present, no need for download!\n",
+             dc->filename,
+             (unsigned long long) dr->offset);
 #endif
   /* already got it! */
   prc.dc = dc;
@@ -439,6 +409,11 @@ check_completed (struct GNUNET_FS_DownloadContext *dc)
     }
   /* All of our children are done, so mark this download done */
   dc->has_finished = GNUNET_YES;
+  if (dc->job_queue != NULL)
+    {
+      GNUNET_FS_dequeue_ (dc->job_queue);
+      dc->job_queue = NULL;
+    }
   GNUNET_FS_download_sync_ (dc);
 
   /* signal completion */
@@ -456,7 +431,8 @@ check_completed (struct GNUNET_FS_DownloadContext *dc)
  * Try it for upward reconstruction of the data.  On success,
  * the top-level block will move to state BRS_DOWNLOAD_UP.
  *
- * @param dr one of our request entries
+ * @param dc context for the download
+ * @param dr download request to match against
  * @param data plaintext data, starting from the beginning of the file
  * @param data_len number of bytes in data
  */ 
@@ -470,7 +446,7 @@ try_match_block (struct GNUNET_FS_DownloadContext *dc,
   unsigned int i;
   char enc[DBLOCK_SIZE];
   struct ContentHashKey chks[CHK_PER_INODE];
-  struct ContentHashKey chk;
+  struct ContentHashKey in_chk;
   struct GNUNET_CRYPTO_AesSessionKey sk;
   struct GNUNET_CRYPTO_AesInitializationVector iv;
   size_t dlen;
@@ -478,7 +454,11 @@ try_match_block (struct GNUNET_FS_DownloadContext *dc,
   struct GNUNET_DISK_FileHandle *fh;
   int complete;
   const char *fn;
+  const char *odata;
+  size_t odata_len;
   
+  odata = data;
+  odata_len = data_len;
   if (BRS_DOWNLOAD_UP == dr->state)
     return;
   if (dr->depth > 0)
@@ -492,6 +472,8 @@ try_match_block (struct GNUNET_FS_DownloadContext *dc,
                           data, data_len);
          if (drc->state != BRS_RECONSTRUCT_META_UP)
            complete = GNUNET_NO;
+         else
+           chks[i] = drc->chk;
        }
       if (GNUNET_YES != complete)
        return;
@@ -507,9 +489,9 @@ try_match_block (struct GNUNET_FS_DownloadContext *dc,
     }
   GNUNET_CRYPTO_hash (&data[dr->offset],
                      dlen,
-                     &chk.key);
-  GNUNET_CRYPTO_hash_to_aes_key (&chk.key, &sk, &iv);
-  if (-1 == GNUNET_CRYPTO_aes_encrypt (data, dlen,
+                     &in_chk.key);
+  GNUNET_CRYPTO_hash_to_aes_key (&in_chk.key, &sk, &iv);
+  if (-1 == GNUNET_CRYPTO_aes_encrypt (&data[dr->offset], dlen,
                                       &sk,
                                       &iv,
                                       enc))
@@ -517,15 +499,15 @@ try_match_block (struct GNUNET_FS_DownloadContext *dc,
       GNUNET_break (0);
       return;
     }
-  GNUNET_CRYPTO_hash (enc, dlen, &chk.query);
+  GNUNET_CRYPTO_hash (enc, dlen, &in_chk.query);
   switch (dr->state)
     {
     case BRS_INIT:
-      dr->chk = chk;
+      dr->chk = in_chk;
       dr->state = BRS_RECONSTRUCT_META_UP;
       break;
     case BRS_CHK_SET:
-      if (0 != memcmp (&chk,
+      if (0 != memcmp (&in_chk,
                       &dr->chk,
                       sizeof (struct ContentHashKey)))
        {
@@ -562,8 +544,8 @@ try_match_block (struct GNUNET_FS_DownloadContext *dc,
        }
       if (data_len != 
          GNUNET_DISK_file_write (fh,
-                                 data,
-                                 data_len))
+                                 odata,
+                                 odata_len))
        {
          GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
                                    "write",
@@ -589,6 +571,12 @@ try_match_block (struct GNUNET_FS_DownloadContext *dc,
       pi.value.download.specifics.progress.data_len = dlen;
       pi.value.download.specifics.progress.depth = 0;
       GNUNET_FS_download_make_status_ (&pi, dc);
+      if ( (NULL != dc->filename) &&
+          (0 != truncate (dc->filename,
+                          GNUNET_ntohll (dc->uri->data.chk.file_length))) )
+       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+                                 "truncate",
+                                 dc->filename);
       check_completed (dc);      
       break;
     default:
@@ -630,6 +618,11 @@ match_full_data (void *cls,
 
   if (type != EXTRACTOR_METATYPE_GNUNET_FULL_DATA) 
     return 0;
+#if DEBUG_DOWNLOAD
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Found %u bytes of FD!\n",
+             (unsigned int) data_len);
+#endif
   if (GNUNET_FS_uri_chk_get_file_size (dc->uri) != data_len)
     {
       GNUNET_break_op (0);
@@ -734,7 +727,7 @@ try_top_down_reconstruction (struct GNUNET_FS_DownloadContext *dc,
     return; /* mismatch */
   if (GNUNET_OK !=
       encrypt_existing_match (dc,
-                             chk,
+                             &dr->chk,
                              dr,
                              block,
                              len,
@@ -763,10 +756,12 @@ try_top_down_reconstruction (struct GNUNET_FS_DownloadContext *dc,
       child_block_size = GNUNET_FS_tree_compute_tree_size (drc->depth);
       GNUNET_assert (0 == (drc->offset - dr->offset) % child_block_size);
       chk_off = (drc->offset - dr->offset) / child_block_size;
-      GNUNET_assert (drc->state == BRS_INIT);
-      drc->state = BRS_CHK_SET;
-      drc->chk = chks[chk_off];
-      try_top_down_reconstruction (dc, drc);
+      if (drc->state == BRS_INIT)      
+       {
+         drc->state = BRS_CHK_SET;
+         drc->chk = chks[chk_off];
+         try_top_down_reconstruction (dc, drc);
+       }
       if (drc->state != BRS_DOWNLOAD_UP)
        up_done = GNUNET_NO; /* children not all done */
     } 
@@ -779,12 +774,7 @@ try_top_down_reconstruction (struct GNUNET_FS_DownloadContext *dc,
  * Schedule the download of the specified block in the tree.
  *
  * @param dc overall download this block belongs to
- * @param chk content-hash-key of the block
- * @param offset offset of the block in the file
- *         (for IBlocks, the offset is the lowest
- *          offset of any DBlock in the subtree under
- *          the IBlock)
- * @param depth depth of the block, 0 is the root of the tree
+ * @param dr request to schedule
  */
 static void
 schedule_block_download (struct GNUNET_FS_DownloadContext *dc,
@@ -827,16 +817,21 @@ schedule_block_download (struct GNUNET_FS_DownloadContext *dc,
              dr->depth,
              GNUNET_h2s (&dr->chk.query));
 #endif
-  GNUNET_CONTAINER_DLL_insert (dc->pending_head,
-                              dc->pending_tail,
-                              dr);
-  dr->is_pending = GNUNET_YES;
+  if (GNUNET_NO !=
+      GNUNET_CONTAINER_multihashmap_contains_value (dc->active,
+                                                   &dr->chk.query,
+                                                   dr))
+    return; /* already active */
   GNUNET_CONTAINER_multihashmap_put (dc->active,
                                     &dr->chk.query,
                                     dr,
                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
   if (dc->client == NULL)
     return; /* download not active */
+  GNUNET_CONTAINER_DLL_insert (dc->pending_head,
+                              dc->pending_tail,
+                              dr);
+  dr->is_pending = GNUNET_YES;
   if (NULL == dc->th)
     dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
                                                  sizeof (struct SearchMessage),
@@ -870,15 +865,14 @@ trigger_recursive_download (void *cls,
 {
   struct GNUNET_FS_DownloadContext *dc = cls;  
   struct GNUNET_FS_DownloadContext *cpos;
-  struct GNUNET_DISK_FileHandle *fh;
   char *temp_name;
-  const char *real_name;
   char *fn;
   char *us;
   char *ext;
   char *dn;
   char *pos;
   char *full_name;
+  char *sfn;
 
   if (NULL == uri)
     return; /* entry for the directory itself */
@@ -942,6 +936,10 @@ trigger_recursive_download (void *cls,
                     (NULL !=
                      strstr (dn + strlen(dn) - strlen(GNUNET_FS_DIRECTORY_EXT),
                              GNUNET_FS_DIRECTORY_EXT)) );
+      sfn = GNUNET_strdup (filename);
+      while ( (strlen (sfn) > 0) &&
+             (filename[strlen(sfn)-1] == '/') )
+       sfn[strlen(sfn)-1] = '\0';
       if ( (strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
           (NULL !=
            strstr (dn + strlen(dn) - strlen(GNUNET_FS_DIRECTORY_EXT),
@@ -957,7 +955,7 @@ trigger_recursive_download (void *cls,
                           "%s%s%s%s",
                           dn,
                           DIR_SEPARATOR_STR,
-                          filename,
+                          sfn,
                           GNUNET_FS_DIRECTORY_EXT);
        }
       else
@@ -966,8 +964,9 @@ trigger_recursive_download (void *cls,
                           "%s%s%s",
                           dn,
                           DIR_SEPARATOR_STR,
-                          filename);
+                          sfn);
        }
+      GNUNET_free (sfn);
       GNUNET_free (dn);
     }
   if ( (full_name != NULL) &&
@@ -983,42 +982,12 @@ trigger_recursive_download (void *cls,
     }
 
   temp_name = NULL;
-  if ( (data != NULL) &&
-       (GNUNET_FS_uri_chk_get_file_size (uri) == length) )
-    {
-      if (full_name == NULL)
-       {
-         temp_name = GNUNET_DISK_mktemp ("gnunet-download-trd");
-         real_name = temp_name;
-       }
-      else
-       {
-         real_name = full_name;
-       }
-      /* write to disk, then trigger normal download which will instantly progress to completion */
-      fh = GNUNET_DISK_file_open (real_name,
-                                 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE | GNUNET_DISK_OPEN_CREATE,
-                                 GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE);
-      if (fh == NULL)
-       {
-         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
-                                   "open",
-                                   real_name);       
-         GNUNET_free (full_name);
-         GNUNET_free_non_null (fn);
-         return;
-       }
-      if (length != 
-         GNUNET_DISK_file_write (fh,
-                                 data,
-                                 length))
-       {
-         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
-                                   "write",
-                                   full_name);       
-       }
-      GNUNET_DISK_file_close (fh);
-    }
+#if DEBUG_DOWNLOAD
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Triggering recursive download of size %llu with %u bytes MD\n",
+             (unsigned long long) GNUNET_FS_uri_chk_get_file_size (uri),
+             (unsigned int) GNUNET_CONTAINER_meta_data_get_serialized_size (meta));
+#endif
   GNUNET_FS_download_start (dc->h,
                            uri,
                            meta,
@@ -1116,10 +1085,9 @@ process_result_with_request (void *cls,
       goto signal_error;
     }
 
-  GNUNET_assert (GNUNET_YES ==
-                GNUNET_CONTAINER_multihashmap_remove (dc->active,
-                                                      &prc->query,
-                                                      dr));
+  (void) GNUNET_CONTAINER_multihashmap_remove (dc->active,
+                                              &prc->query,
+                                              dr);
   if (GNUNET_YES == dr->is_pending)
     {
       GNUNET_CONTAINER_DLL_remove (dc->pending_head,
@@ -1240,6 +1208,8 @@ process_result_with_request (void *cls,
   pi.value.download.specifics.progress.depth = dr->depth;
   GNUNET_FS_download_make_status_ (&pi, dc);
   GNUNET_assert (dc->completed <= dc->length);
+  if (dr->depth == 0) 
+    propagate_up (dr);
 
   if (dc->completed == dc->length)
     {
@@ -1258,17 +1228,11 @@ process_result_with_request (void *cls,
                                      "truncate",
                                      dc->filename);
        }
-      if (dc->job_queue != NULL)
-       {
-         GNUNET_FS_dequeue_ (dc->job_queue);
-         dc->job_queue = NULL;
-       }
       GNUNET_assert (dr->depth == 0);
       check_completed (dc);
     }
   if (dr->depth == 0) 
     {
-      propagate_up (dr);
       /* bottom of the tree, no child downloads possible, just sync */
       GNUNET_FS_download_sync_ (dc);
       return GNUNET_YES;
@@ -1284,9 +1248,6 @@ process_result_with_request (void *cls,
   chk = (struct ContentHashKey*) pt;
   for (i=(prc->size / sizeof(struct ContentHashKey))-1;i>=0;i--)
     {
-      off = compute_dblock_offset (dr->offset,
-                                  dr->depth,
-                                  i);
       drc = dr->children[i];
       switch (drc->state)
        {
@@ -1337,6 +1298,7 @@ process_result_with_request (void *cls,
       dc->th = NULL;
     }
   GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
+  dc->in_receive = GNUNET_NO;
   dc->client = NULL;
   GNUNET_FS_free_download_request_ (dc->top_request);
   dc->top_request = NULL;
@@ -1498,6 +1460,14 @@ transmit_download_request (void *cls,
                                                    dc); 
       GNUNET_assert (dc->th != NULL);
     }
+  if (GNUNET_NO == dc->in_receive)
+    {
+      dc->in_receive = GNUNET_YES;
+      GNUNET_CLIENT_receive (dc->client,
+                            &receive_results,
+                            dc,
+                            GNUNET_TIME_UNIT_FOREVER_REL);
+    }
   return msize;
 }
 
@@ -1537,10 +1507,6 @@ do_reconnect (void *cls,
                                                    dc);
       GNUNET_assert (dc->th != NULL);
     }
-  GNUNET_CLIENT_receive (client,
-                        &receive_results,
-                        dc,
-                        GNUNET_TIME_UNIT_FOREVER_REL);
 }
 
 
@@ -1560,6 +1526,8 @@ retry_entry (void *cls,
   struct GNUNET_FS_DownloadContext *dc = cls;
   struct DownloadRequest *dr = entry;
 
+  dr->next = NULL;
+  dr->prev = NULL;
   GNUNET_CONTAINER_DLL_insert (dc->pending_head,
                               dc->pending_tail,
                               dr);
@@ -1597,6 +1565,7 @@ try_reconnect (struct GNUNET_FS_DownloadContext *dc)
                                             &retry_entry,
                                             dc);
       GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
+      dc->in_receive = GNUNET_NO;
       dc->client = NULL;
     }
 #if DEBUG_DOWNLOAD
@@ -1631,12 +1600,10 @@ activate_fs_download (void *cls,
   GNUNET_assert (dc->client == NULL);
   GNUNET_assert (dc->th == NULL);
   dc->client = client;
-  GNUNET_CLIENT_receive (client,
-                        &receive_results,
-                        dc,
-                        GNUNET_TIME_UNIT_FOREVER_REL);
   pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE;
   GNUNET_FS_download_make_status_ (&pi, dc);
+  dc->pending_head = NULL;
+  dc->pending_tail = NULL;
   GNUNET_CONTAINER_multihashmap_iterate (dc->active,
                                         &retry_entry,
                                         dc);
@@ -1680,8 +1647,11 @@ deactivate_fs_download (void *cls)
   if (NULL != dc->client)
     {
       GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
+      dc->in_receive = GNUNET_NO;
       dc->client = NULL;
     }
+  dc->pending_head = NULL;
+  dc->pending_tail = NULL;
   pi.status = GNUNET_FS_STATUS_DOWNLOAD_INACTIVE;
   GNUNET_FS_download_make_status_ (&pi, dc);
 }
@@ -1699,7 +1669,7 @@ deactivate_fs_download (void *cls)
  * @param file_start_offset desired starting offset for the download
  *             in the original file; requesting tree should not contain
  *             DBLOCKs prior to the file_start_offset
- * @param file_length desired number of bytes the user wanted to access
+ * @param desired_length desired number of bytes the user wanted to access
  *        (from file_start_offset).  Resulting tree should not contain
  *        DBLOCKs after file_start_offset + file_length.
  * @return download request tree for the given range of DBLOCKs at
@@ -1797,6 +1767,7 @@ reconstruct_cont (void *cls,
 /**
  * Task requesting the next block from the tree encoder.
  *
+ * @param cls the 'struct GNUJNET_FS_DownloadContext' we're processing
  * @param tc task context
  */
 static void
@@ -1977,9 +1948,7 @@ GNUNET_FS_download_start_task_ (void *cls,
       /* no bytes required! */
       if (dc->filename != NULL) 
        {
-         fh = GNUNET_DISK_file_open (dc->filename != NULL 
-                                     ? dc->filename 
-                                     : dc->temp_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)) 
@@ -2021,6 +1990,11 @@ GNUNET_FS_download_start_task_ (void *cls,
       if (dc->rfh != NULL)
        {
          /* first, try top-down */
+#if DEBUG_DOWNLOAD
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     "Trying top-down reconstruction for `%s'\n",
+                     dc->filename);
+#endif
          try_top_down_reconstruction (dc, dc->top_request);
          switch (dc->top_request->state)
            {
@@ -2030,16 +2004,6 @@ GNUNET_FS_download_start_task_ (void *cls,
              break; /* normal, some blocks already down */
            case BRS_DOWNLOAD_UP:
              /* already done entirely, party! */
-             dc->completed = dc->length;
-             GNUNET_FS_download_sync_ (dc);
-             pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
-             /* slightly ugly: no data provided to callee; maybe mmap the
-                file instead? Or is 'data' pure convenience!? */
-             pi.value.download.specifics.progress.data = NULL;
-             pi.value.download.specifics.progress.offset = dc->offset;
-             pi.value.download.specifics.progress.data_len = dc->length;
-             pi.value.download.specifics.progress.depth = 0;
-             GNUNET_FS_download_make_status_ (&pi, dc);
              if (dc->rfh != NULL)
                {
                  /* avoid hanging on to file handle longer than 
@@ -2047,7 +2011,6 @@ GNUNET_FS_download_start_task_ (void *cls,
                  GNUNET_DISK_file_close (dc->rfh);
                  dc->rfh = NULL;
                }
-             check_completed (dc);
              return;      
            case BRS_ERROR:
              GNUNET_asprintf (&dc->emsg,
@@ -2064,8 +2027,15 @@ GNUNET_FS_download_start_task_ (void *cls,
        }
     }
   /* attempt reconstruction from meta data */
-  if (GNUNET_FS_uri_chk_get_file_size (dc->uri) <= MAX_INLINE_SIZE)
+  if ( (GNUNET_FS_uri_chk_get_file_size (dc->uri) <= MAX_INLINE_SIZE) &&
+       (NULL != dc->meta) )
     {
+#if DEBUG_DOWNLOAD
+      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));
+#endif
       GNUNET_CONTAINER_meta_data_iterate (dc->meta,
                                          &match_full_data,
                                          dc);
@@ -2084,6 +2054,11 @@ GNUNET_FS_download_start_task_ (void *cls,
   if (dc->rfh != NULL)
     {
       /* finally, try bottom-up */
+#if DEBUG_DOWNLOAD
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Trying bottom-up reconstruction of file `%s'\n",
+                 dc->filename);
+#endif
       dc->te = GNUNET_FS_tree_encoder_create (dc->h,
                                              dc->old_file_size,
                                              dc,