stuff
[oweals/gnunet.git] / src / fs / fs_download.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file applications/fs/ecrs/download.c
22  * @brief DOWNLOAD helper methods (which do the real work).
23  * @author Christian Grothoff
24  */
25
26 #include "platform.h"
27 #include "gnunet_protocols.h"
28 #include "gnunet_ecrs_lib.h"
29 #include "gnunet_fs_lib.h"
30 #include "gnunet_identity_lib.h"
31 #include "ecrs_core.h"
32 #include "ecrs.h"
33 #include "fs.h"
34 #include "tree.h"
35
36 #define DEBUG_DOWNLOAD GNUNET_NO
37
38 /**
39  * Node-specific data (not shared, keep small!). 152 bytes.
40  * Nodes are kept in a doubly-linked list.
41  */
42 struct Node
43 {
44   /**
45    * Pointer to shared data between all nodes (request manager,
46    * progress data, etc.).
47    */
48   struct GNUNET_ECRS_DownloadContext *ctx;
49
50   /**
51    * Previous entry in DLL.
52    */
53   struct Node *prev;
54
55   /**
56    * Next entry in DLL.
57    */
58   struct Node *next;
59
60   /**
61    * What is the GNUNET_EC_ContentHashKey for this block?
62    */
63   GNUNET_EC_ContentHashKey chk;
64
65   /**
66    * At what offset (on the respective level!) is this
67    * block?
68    */
69   unsigned long long offset;
70
71   /**
72    * 0 for dblocks, >0 for iblocks.
73    */
74   unsigned int level;
75
76 };
77
78 /**
79  * @brief structure that keeps track of currently pending requests for
80  *        a download
81  *
82  * Handle to the state of a request manager.  Here we keep track of
83  * which queries went out with which priorities and which nodes in
84  * the merkle-tree are waiting for the replies.
85  */
86 struct GNUNET_ECRS_DownloadContext
87 {
88
89   /**
90    * Total number of bytes in the file.
91    */
92   unsigned long long total;
93
94   /**
95    * Number of bytes already obtained
96    */
97   unsigned long long completed;
98
99   /**
100    * Starting-offset in file (for partial download)
101    */
102   unsigned long long offset;
103
104   /**
105    * Length of the download (starting at offset).
106    */
107   unsigned long long length;
108
109   /**
110    * Time download was started.
111    */
112   GNUNET_CronTime startTime;
113
114   /**
115    * Doubly linked list of all pending requests (head)
116    */
117   struct Node *head;
118
119   /**
120    * Doubly linked list of all pending requests (tail)
121    */
122   struct Node *tail;
123
124   /**
125    * FSLIB context for issuing requests.
126    */
127   struct GNUNET_FS_SearchContext *sctx;
128
129   /**
130    * Context for error reporting.
131    */
132   struct GNUNET_GE_Context *ectx;
133
134   /**
135    * Configuration information.
136    */
137   struct GNUNET_GC_Configuration *cfg;
138
139   /**
140    * The file handle.
141    */
142   int handle;
143
144   /**
145    * Do we exclusively own this sctx?
146    */
147   int my_sctx;
148
149   /**
150    * The base-filename
151    */
152   char *filename;
153
154   /**
155    * Main thread running the operation.
156    */
157   struct GNUNET_ThreadHandle *main;
158
159   /**
160    * Function to call when we make progress.
161    */
162   GNUNET_ECRS_DownloadProgressCallback dpcb;
163
164   /**
165    * Extra argument to dpcb.
166    */
167   void *dpcbClosure;
168
169   /**
170    * Identity of the peer having the content, or all-zeros
171    * if we don't know of such a peer.
172    */
173   GNUNET_PeerIdentity target;
174
175   /**
176    * Abort?  Flag that can be set at any time
177    * to abort the RM as soon as possible.  Set
178    * to GNUNET_YES during orderly shutdown,
179    * set to GNUNET_SYSERR on error.
180    */
181   int abortFlag;
182
183   /**
184    * Do we have a specific peer from which we download
185    * from?
186    */
187   int have_target;
188
189   /**
190    * Desired anonymity level for the download.
191    */
192   unsigned int anonymityLevel;
193
194   /**
195    * The depth of the file-tree.
196    */
197   unsigned int treedepth;
198
199 };
200
201 static int
202 content_receive_callback (const GNUNET_HashCode * query,
203                           const GNUNET_DatastoreValue * reply, void *cls,
204                           unsigned long long uid);
205
206
207 /**
208  * Close the files and free the associated resources.
209  *
210  * @param self reference to the download context
211  */
212 static void
213 free_request_manager (struct GNUNET_ECRS_DownloadContext *rm)
214 {
215   struct Node *pos;
216
217   if (rm->abortFlag == GNUNET_NO)
218     rm->abortFlag = GNUNET_YES;
219   if (rm->my_sctx == GNUNET_YES)
220     GNUNET_FS_destroy_search_context (rm->sctx);
221   else
222     GNUNET_FS_suspend_search_context (rm->sctx);
223   while (rm->head != NULL)
224     {
225       pos = rm->head;
226       GNUNET_DLL_remove (rm->head, rm->tail, pos);
227       if (rm->my_sctx != GNUNET_YES)
228         GNUNET_FS_stop_search (rm->sctx, &content_receive_callback, pos);
229       GNUNET_free (pos);
230     }
231   if (rm->my_sctx != GNUNET_YES)
232     GNUNET_FS_resume_search_context (rm->sctx);
233   GNUNET_GE_ASSERT (NULL, rm->tail == NULL);
234   if (rm->handle >= 0)
235     CLOSE (rm->handle);
236   if (rm->main != NULL)
237     GNUNET_thread_release_self (rm->main);
238   GNUNET_free_non_null (rm->filename);
239   rm->sctx = NULL;
240   GNUNET_free (rm);
241 }
242
243 /**
244  * Read method.
245  *
246  * @param self reference to the download context
247  * @param level level in the tree to read/write at
248  * @param pos position where to read or write
249  * @param buf where to read from or write to
250  * @param len how many bytes to read or write
251  * @return number of bytes read, GNUNET_SYSERR on error
252  */
253 static int
254 read_from_files (struct GNUNET_ECRS_DownloadContext *self,
255                  unsigned int level,
256                  unsigned long long pos, void *buf, unsigned int len)
257 {
258   if ((level > 0) || (self->handle == -1))
259     return GNUNET_SYSERR;
260   LSEEK (self->handle, pos, SEEK_SET);
261   return READ (self->handle, buf, len);
262 }
263
264 /**
265  * Write method.
266  *
267  * @param self reference to the download context
268  * @param level level in the tree to write to
269  * @param pos position where to  write
270  * @param buf where to write to
271  * @param len how many bytes to write
272  * @return number of bytes written, GNUNET_SYSERR on error
273  */
274 static int
275 write_to_files (struct GNUNET_ECRS_DownloadContext *self,
276                 unsigned int level,
277                 unsigned long long pos, void *buf, unsigned int len)
278 {
279   int ret;
280
281   if (level > 0)
282     return len;                 /* lie -- no more temps */
283   if (self->handle == -1)
284     return len;
285   LSEEK (self->handle, pos, SEEK_SET);
286   ret = WRITE (self->handle, buf, len);
287   if (ret != len)
288     GNUNET_GE_LOG_STRERROR_FILE (self->ectx,
289                                  GNUNET_GE_ERROR | GNUNET_GE_BULK |
290                                  GNUNET_GE_USER, "write", self->filename);
291   return ret;
292 }
293
294 /**
295  * Queue a request for execution.
296  *
297  * @param rm the request manager struct from createRequestManager
298  * @param node the node to call once a reply is received
299  */
300 static void
301 add_request (struct Node *node)
302 {
303   struct GNUNET_ECRS_DownloadContext *rm = node->ctx;
304
305   GNUNET_DLL_insert (rm->head, rm->tail, node);
306   GNUNET_FS_start_search (rm->sctx,
307                           rm->have_target == GNUNET_NO ? NULL : &rm->target,
308                           GNUNET_ECRS_BLOCKTYPE_DATA, 1,
309                           &node->chk.query,
310                           rm->anonymityLevel,
311                           &content_receive_callback, node);
312 }
313
314 static void
315 signal_abort (struct GNUNET_ECRS_DownloadContext *rm, const char *msg)
316 {
317   rm->abortFlag = GNUNET_SYSERR;
318   if ((rm->head != NULL) && (rm->dpcb != NULL))
319     rm->dpcb (rm->length + 1, 0, 0, 0, msg, 0, rm->dpcbClosure);
320   GNUNET_thread_stop_sleep (rm->main);
321 }
322
323 /**
324  * Dequeue a request.
325  *
326  * @param self the request manager struct from createRequestManager
327  * @param node the block for which the request is canceled
328  */
329 static void
330 delete_node (struct Node *node)
331 {
332   struct GNUNET_ECRS_DownloadContext *rm = node->ctx;
333
334   GNUNET_DLL_remove (rm->head, rm->tail, node);
335   GNUNET_free (node);
336   if (rm->head == NULL)
337     GNUNET_thread_stop_sleep (rm->main);
338 }
339
340 /**
341  * Compute how many bytes of data are stored in
342  * this node.
343  */
344 static unsigned int
345 get_node_size (const struct Node *node)
346 {
347   unsigned int i;
348   unsigned int ret;
349   unsigned long long rsize;
350   unsigned long long spos;
351   unsigned long long epos;
352
353   GNUNET_GE_ASSERT (node->ctx->ectx, node->offset < node->ctx->total);
354   if (node->level == 0)
355     {
356       ret = GNUNET_ECRS_DBLOCK_SIZE;
357       if (node->offset + (unsigned long long) ret > node->ctx->total)
358         ret = (unsigned int) (node->ctx->total - node->offset);
359 #if DEBUG_DOWNLOAD
360       GNUNET_GE_LOG (node->ctx->rm->ectx,
361                      GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
362                      "Node at offset %llu and level %d has size %u\n",
363                      node->offset, node->level, ret);
364 #endif
365       return ret;
366     }
367   rsize = GNUNET_ECRS_DBLOCK_SIZE;
368   for (i = 0; i < node->level - 1; i++)
369     rsize *= GNUNET_ECRS_CHK_PER_INODE;
370   spos = rsize * (node->offset / sizeof (GNUNET_EC_ContentHashKey));
371   epos = spos + rsize * GNUNET_ECRS_CHK_PER_INODE;
372   if (epos > node->ctx->total)
373     epos = node->ctx->total;
374   ret = (epos - spos) / rsize;
375   if (ret * rsize < epos - spos)
376     ret++;                      /* need to round up! */
377 #if DEBUG_DOWNLOAD
378   GNUNET_GE_LOG (node->ctx->rm->ectx,
379                  GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
380                  "Node at offset %llu and level %d has size %u\n",
381                  node->offset, node->level,
382                  ret * sizeof (GNUNET_EC_ContentHashKey));
383 #endif
384   return ret * sizeof (GNUNET_EC_ContentHashKey);
385 }
386
387 /**
388  * Notify client about progress.
389  */
390 static void
391 notify_client_about_progress (const struct Node *node,
392                               const char *data, unsigned int size)
393 {
394   struct GNUNET_ECRS_DownloadContext *rm = node->ctx;
395   GNUNET_CronTime eta;
396
397   if ((rm->abortFlag != GNUNET_NO) || (node->level != 0))
398     return;
399   rm->completed += size;
400   eta = GNUNET_get_time ();
401   if (rm->completed > 0)
402     eta = (GNUNET_CronTime) (rm->startTime +
403                              (((double) (eta - rm->startTime) /
404                                (double) rm->completed)) *
405                              (double) rm->length);
406   if (rm->dpcb != NULL)
407     rm->dpcb (rm->length,
408               rm->completed, eta, node->offset, data, size, rm->dpcbClosure);
409 }
410
411
412 /**
413  * DOWNLOAD children of this GNUNET_EC_IBlock.
414  *
415  * @param node the node for which the children should be downloaded
416  * @param data data for the node
417  * @param size size of data
418  */
419 static void iblock_download_children (const struct Node *node,
420                                       const char *data, unsigned int size);
421
422 /**
423  * Check if self block is already present on the drive.  If the block
424  * is a dblock and present, the ProgressModel is notified. If the
425  * block is present and it is an iblock, downloading the children is
426  * triggered.
427  *
428  * Also checks if the block is within the range of blocks
429  * that we are supposed to download.  If not, the method
430  * returns as if the block is present but does NOT signal
431  * progress.
432  *
433  * @param node that is checked for presence
434  * @return GNUNET_YES if present, GNUNET_NO if not.
435  */
436 static int
437 check_node_present (const struct Node *node)
438 {
439   int res;
440   int ret;
441   char *data;
442   unsigned int size;
443   GNUNET_HashCode hc;
444
445   size = get_node_size (node);
446   /* first check if node is within range.
447      For now, keeping it simple, we only do
448      this for level-0 nodes */
449   if ((node->level == 0) &&
450       ((node->offset + size < node->ctx->offset) ||
451        (node->offset >= node->ctx->offset + node->ctx->length)))
452     return GNUNET_YES;
453   data = GNUNET_malloc (size);
454   ret = GNUNET_NO;
455   res = read_from_files (node->ctx, node->level, node->offset, data, size);
456   if (res == size)
457     {
458       GNUNET_hash (data, size, &hc);
459       if (0 == memcmp (&hc, &node->chk.key, sizeof (GNUNET_HashCode)))
460         {
461           notify_client_about_progress (node, data, size);
462           if (node->level > 0)
463             iblock_download_children (node, data, size);
464           ret = GNUNET_YES;
465         }
466     }
467   GNUNET_free (data);
468   return ret;
469 }
470
471 /**
472  * DOWNLOAD children of this GNUNET_EC_IBlock.
473  *
474  * @param node the node that should be downloaded
475  */
476 static void
477 iblock_download_children (const struct Node *node,
478                           const char *data, unsigned int size)
479 {
480   struct GNUNET_GE_Context *ectx = node->ctx->ectx;
481   int i;
482   struct Node *child;
483   unsigned int childcount;
484   const GNUNET_EC_ContentHashKey *chks;
485   unsigned int levelSize;
486   unsigned long long baseOffset;
487
488   GNUNET_GE_ASSERT (ectx, node->level > 0);
489   childcount = size / sizeof (GNUNET_EC_ContentHashKey);
490   if (size != childcount * sizeof (GNUNET_EC_ContentHashKey))
491     {
492       GNUNET_GE_BREAK (ectx, 0);
493       return;
494     }
495   if (node->level == 1)
496     {
497       levelSize = GNUNET_ECRS_DBLOCK_SIZE;
498       baseOffset =
499         node->offset / sizeof (GNUNET_EC_ContentHashKey) *
500         GNUNET_ECRS_DBLOCK_SIZE;
501     }
502   else
503     {
504       levelSize =
505         sizeof (GNUNET_EC_ContentHashKey) * GNUNET_ECRS_CHK_PER_INODE;
506       baseOffset = node->offset * GNUNET_ECRS_CHK_PER_INODE;
507     }
508   chks = (const GNUNET_EC_ContentHashKey *) data;
509   for (i = 0; i < childcount; i++)
510     {
511       child = GNUNET_malloc (sizeof (struct Node));
512       child->ctx = node->ctx;
513       child->chk = chks[i];
514       child->offset = baseOffset + i * levelSize;
515       GNUNET_GE_ASSERT (ectx, child->offset < node->ctx->total);
516       child->level = node->level - 1;
517       GNUNET_GE_ASSERT (ectx, (child->level != 0) ||
518                         ((child->offset % GNUNET_ECRS_DBLOCK_SIZE) == 0));
519       if (GNUNET_NO == check_node_present (child))
520         add_request (child);
521       else
522         GNUNET_free (child);    /* done already! */
523     }
524 }
525
526
527 /**
528  * Decrypts a given data block
529  *
530  * @param data represents the data block
531  * @param hashcode represents the key concatenated with the initial
532  *        value used in the alg
533  * @param result where to store the result (encrypted block)
534  * @returns GNUNET_OK on success, GNUNET_SYSERR on error
535  */
536 static int
537 decrypt_content (const char *data,
538                  unsigned int size, const GNUNET_HashCode * hashcode,
539                  char *result)
540 {
541   GNUNET_AES_InitializationVector iv;
542   GNUNET_AES_SessionKey skey;
543
544   /* get key and init value from the GNUNET_HashCode */
545   GNUNET_hash_to_AES_key (hashcode, &skey, &iv);
546   return GNUNET_AES_decrypt (&skey, data, size, &iv, result);
547 }
548
549 /**
550  * We received a GNUNET_EC_ContentHashKey reply for a block. Decrypt.  Note
551  * that the caller (fslib) has already aquired the
552  * RM lock (we sometimes aquire it again in callees,
553  * mostly because our callees could be also be theoretically
554  * called from elsewhere).
555  *
556  * @param cls the node for which the reply is given, freed in
557  *        the function!
558  * @param query the query for which reply is the answer
559  * @param reply the reply
560  * @return GNUNET_OK if the reply was valid, GNUNET_SYSERR on error
561  */
562 static int
563 content_receive_callback (const GNUNET_HashCode * query,
564                           const GNUNET_DatastoreValue * reply, void *cls,
565                           unsigned long long uid)
566 {
567   struct Node *node = cls;
568   struct GNUNET_ECRS_DownloadContext *rm = node->ctx;
569   struct GNUNET_GE_Context *ectx = rm->ectx;
570   GNUNET_HashCode hc;
571   unsigned int size;
572   char *data;
573
574   if (rm->abortFlag != GNUNET_NO)
575     return GNUNET_SYSERR;
576   GNUNET_GE_ASSERT (ectx,
577                     0 == memcmp (query, &node->chk.query,
578                                  sizeof (GNUNET_HashCode)));
579   size = ntohl (reply->size) - sizeof (GNUNET_DatastoreValue);
580   if ((size <= sizeof (GNUNET_EC_DBlock)) ||
581       (size - sizeof (GNUNET_EC_DBlock) != get_node_size (node)))
582     {
583       GNUNET_GE_BREAK (ectx, 0);
584       return GNUNET_SYSERR;     /* invalid size! */
585     }
586   size -= sizeof (GNUNET_EC_DBlock);
587   data = GNUNET_malloc (size);
588   if (GNUNET_SYSERR ==
589       decrypt_content ((const char *)
590                        &((const GNUNET_EC_DBlock *) &reply[1])[1], size,
591                        &node->chk.key, data))
592     GNUNET_GE_ASSERT (ectx, 0);
593   GNUNET_hash (data, size, &hc);
594   if (0 != memcmp (&hc, &node->chk.key, sizeof (GNUNET_HashCode)))
595     {
596       GNUNET_free (data);
597       GNUNET_GE_BREAK (ectx, 0);
598       signal_abort (rm,
599                     _("Decrypted content does not match key. "
600                       "This is either a bug or a maliciously inserted "
601                       "file. Download aborted.\n"));
602       return GNUNET_SYSERR;
603     }
604   if (size != write_to_files (rm, node->level, node->offset, data, size))
605     {
606       GNUNET_GE_LOG_STRERROR (ectx,
607                               GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
608                               GNUNET_GE_USER | GNUNET_GE_BULK, "WRITE");
609       signal_abort (rm, _("IO error."));
610       return GNUNET_SYSERR;
611     }
612   notify_client_about_progress (node, data, size);
613   if (node->level > 0)
614     iblock_download_children (node, data, size);
615   GNUNET_free (data);
616   /* request satisfied, stop requesting! */
617   delete_node (node);
618   return GNUNET_OK;
619 }
620
621
622 /**
623  * Helper function to sanitize filename
624  * and create necessary directories.
625  */
626 static char *
627 get_real_download_filename (struct GNUNET_GE_Context *ectx,
628                             const char *filename)
629 {
630   struct stat buf;
631   char *realFN;
632   char *path;
633   char *pos;
634
635   if ((filename[strlen (filename) - 1] == '/') ||
636       (filename[strlen (filename) - 1] == '\\'))
637     {
638       realFN =
639         GNUNET_malloc (strlen (filename) + strlen (GNUNET_DIRECTORY_EXT));
640       strcpy (realFN, filename);
641       realFN[strlen (filename) - 1] = '\0';
642       strcat (realFN, GNUNET_DIRECTORY_EXT);
643     }
644   else
645     {
646       realFN = GNUNET_strdup (filename);
647     }
648   path = GNUNET_malloc (strlen (realFN) * strlen (GNUNET_DIRECTORY_EXT) + 1);
649   strcpy (path, realFN);
650   pos = path;
651   while (*pos != '\0')
652     {
653       if (*pos == DIR_SEPARATOR)
654         {
655           *pos = '\0';
656           if ((0 == STAT (path, &buf)) && (!S_ISDIR (buf.st_mode)))
657             {
658               *pos = DIR_SEPARATOR;
659               memmove (pos + strlen (GNUNET_DIRECTORY_EXT),
660                        pos, strlen (pos));
661               memcpy (pos,
662                       GNUNET_DIRECTORY_EXT, strlen (GNUNET_DIRECTORY_EXT));
663               pos += strlen (GNUNET_DIRECTORY_EXT);
664             }
665           else
666             {
667               *pos = DIR_SEPARATOR;
668             }
669         }
670       pos++;
671     }
672   GNUNET_free (realFN);
673   return path;
674 }
675
676 /* ***************** main method **************** */
677
678
679 /**
680  * Download parts of a file.  Note that this will store
681  * the blocks at the respective offset in the given file.
682  * Also, the download is still using the blocking of the
683  * underlying ECRS encoding.  As a result, the download
684  * may *write* outside of the given boundaries (if offset
685  * and length do not match the 32k ECRS block boundaries).
686  * <p>
687  *
688  * This function should be used to focus a download towards a
689  * particular portion of the file (optimization), not to strictly
690  * limit the download to exactly those bytes.
691  *
692  * @param uri the URI of the file (determines what to download)
693  * @param filename where to store the file
694  * @param no_temporaries set to GNUNET_YES to disallow generation of temporary files
695  * @param start starting offset
696  * @param length length of the download (starting at offset)
697  */
698 struct GNUNET_ECRS_DownloadContext *
699 GNUNET_ECRS_file_download_partial_start (struct GNUNET_GE_Context *ectx,
700                                          struct GNUNET_GC_Configuration *cfg,
701                                          struct GNUNET_FS_SearchContext *sc,
702                                          const struct GNUNET_ECRS_URI *uri,
703                                          const char *filename,
704                                          unsigned long long offset,
705                                          unsigned long long length,
706                                          unsigned int anonymityLevel,
707                                          int no_temporaries,
708                                          GNUNET_ECRS_DownloadProgressCallback
709                                          dpcb, void *dpcbClosure)
710 {
711   struct GNUNET_ECRS_DownloadContext *rm;
712   struct stat buf;
713   struct Node *top;
714   int ret;
715
716   if ((!GNUNET_ECRS_uri_test_chk (uri)) && (!GNUNET_ECRS_uri_test_loc (uri)))
717     {
718       GNUNET_GE_BREAK (ectx, 0);
719       return NULL;
720     }
721   rm = GNUNET_malloc (sizeof (struct GNUNET_ECRS_DownloadContext));
722   memset (rm, 0, sizeof (struct GNUNET_ECRS_DownloadContext));
723   if (sc == NULL)
724     {
725       rm->sctx = GNUNET_FS_create_search_context (ectx, cfg);
726       if (rm->sctx == NULL)
727         {
728           GNUNET_free (rm);
729           return NULL;
730         }
731       rm->my_sctx = GNUNET_YES;
732     }
733   else
734     {
735       rm->sctx = sc;
736       rm->my_sctx = GNUNET_NO;
737     }
738   rm->ectx = ectx;
739   rm->cfg = cfg;
740   rm->startTime = GNUNET_get_time ();
741   rm->anonymityLevel = anonymityLevel;
742   rm->offset = offset;
743   rm->length = length;
744   rm->dpcb = dpcb;
745   rm->dpcbClosure = dpcbClosure;
746   rm->main = GNUNET_thread_get_self ();
747   rm->total = GNUNET_ntohll (uri->data.fi.file_length);
748   rm->filename =
749     filename != NULL ? get_real_download_filename (ectx, filename) : NULL;
750
751   if ((rm->filename != NULL) &&
752       (GNUNET_SYSERR ==
753        GNUNET_disk_directory_create_for_file (ectx, rm->filename)))
754     {
755       free_request_manager (rm);
756       return NULL;
757     }
758   if (0 == rm->total)
759     {
760       if (rm->filename != NULL)
761         {
762           ret = GNUNET_disk_file_open (ectx,
763                                        rm->filename,
764                                        O_CREAT | O_WRONLY | O_TRUNC,
765                                        S_IRUSR | S_IWUSR);
766           if (ret == -1)
767             {
768               free_request_manager (rm);
769               return NULL;
770             }
771           CLOSE (ret);
772         }
773       dpcb (0, 0, rm->startTime, 0, NULL, 0, dpcbClosure);
774       free_request_manager (rm);
775       return NULL;
776     }
777   rm->treedepth = GNUNET_ECRS_compute_depth (rm->total);
778   if ((NULL != rm->filename) &&
779       ((0 == STAT (rm->filename, &buf))
780        && ((size_t) buf.st_size > rm->total)))
781     {
782       /* if exists and oversized, truncate */
783       if (truncate (rm->filename, rm->total) != 0)
784         {
785           GNUNET_GE_LOG_STRERROR_FILE (ectx,
786                                        GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
787                                        GNUNET_GE_BULK, "truncate",
788                                        rm->filename);
789           free_request_manager (rm);
790           return NULL;
791         }
792     }
793   if (rm->filename != NULL)
794     {
795       rm->handle = GNUNET_disk_file_open (ectx,
796                                           rm->filename,
797                                           O_CREAT | O_RDWR,
798                                           S_IRUSR | S_IWUSR);
799       if (rm->handle < 0)
800         {
801           free_request_manager (rm);
802           return NULL;
803         }
804     }
805   else
806     rm->handle = -1;
807   if (GNUNET_ECRS_uri_test_loc (uri))
808     {
809       GNUNET_hash (&uri->data.loc.peer, sizeof (GNUNET_RSA_PublicKey),
810                    &rm->target.hashPubKey);
811       rm->have_target = GNUNET_YES;
812     }
813   top = GNUNET_malloc (sizeof (struct Node));
814   memset (top, 0, sizeof (struct Node));
815   top->ctx = rm;
816   top->chk = uri->data.fi.chk;
817   top->offset = 0;
818   top->level = rm->treedepth;
819   if (GNUNET_NO == check_node_present (top))
820     add_request (top);
821   else
822     GNUNET_free (top);
823   return rm;
824 }
825
826 int
827 GNUNET_ECRS_file_download_partial_stop (struct GNUNET_ECRS_DownloadContext
828                                         *rm)
829 {
830   int ret;
831
832   ret = rm->abortFlag;
833   free_request_manager (rm);
834   if (ret == GNUNET_NO)
835     ret = GNUNET_OK;            /* normal termination */
836   return ret;
837 }
838
839 /**
840  * Download parts of a file.  Note that this will store
841  * the blocks at the respective offset in the given file.
842  * Also, the download is still using the blocking of the
843  * underlying ECRS encoding.  As a result, the download
844  * may *write* outside of the given boundaries (if offset
845  * and length do not match the 32k ECRS block boundaries).
846  * <p>
847  *
848  * This function should be used to focus a download towards a
849  * particular portion of the file (optimization), not to strictly
850  * limit the download to exactly those bytes.
851  *
852  * @param uri the URI of the file (determines what to download)
853  * @param filename where to store the file
854  * @param no_temporaries set to GNUNET_YES to disallow generation of temporary files
855  * @param start starting offset
856  * @param length length of the download (starting at offset)
857  */
858 int
859 GNUNET_ECRS_file_download_partial (struct GNUNET_GE_Context *ectx,
860                                    struct GNUNET_GC_Configuration *cfg,
861                                    const struct GNUNET_ECRS_URI *uri,
862                                    const char *filename,
863                                    unsigned long long offset,
864                                    unsigned long long length,
865                                    unsigned int anonymityLevel,
866                                    int no_temporaries,
867                                    GNUNET_ECRS_DownloadProgressCallback dpcb,
868                                    void *dpcbClosure,
869                                    GNUNET_ECRS_TestTerminate tt,
870                                    void *ttClosure)
871 {
872   struct GNUNET_ECRS_DownloadContext *rm;
873   int ret;
874
875   if (length == 0)
876     return GNUNET_OK;
877   rm = GNUNET_ECRS_file_download_partial_start (ectx,
878                                                 cfg,
879                                                 NULL,
880                                                 uri,
881                                                 filename,
882                                                 offset,
883                                                 length,
884                                                 anonymityLevel,
885                                                 no_temporaries,
886                                                 dpcb, dpcbClosure);
887   if (rm == NULL)
888     return GNUNET_SYSERR;
889   while ((GNUNET_OK == tt (ttClosure)) &&
890          (GNUNET_YES != GNUNET_shutdown_test ()) &&
891          (rm->abortFlag == GNUNET_NO) && (rm->head != NULL))
892     GNUNET_thread_sleep (5 * GNUNET_CRON_SECONDS);
893   ret = GNUNET_ECRS_file_download_partial_stop (rm);
894   return ret;
895 }
896
897 /**
898  * Download a file (simplified API).
899  *
900  * @param uri the URI of the file (determines what to download)
901  * @param filename where to store the file
902  */
903 int
904 GNUNET_ECRS_file_download (struct GNUNET_GE_Context *ectx,
905                            struct GNUNET_GC_Configuration *cfg,
906                            const struct GNUNET_ECRS_URI *uri,
907                            const char *filename,
908                            unsigned int anonymityLevel,
909                            GNUNET_ECRS_DownloadProgressCallback dpcb,
910                            void *dpcbClosure, GNUNET_ECRS_TestTerminate tt,
911                            void *ttClosure)
912 {
913   return GNUNET_ECRS_file_download_partial (ectx,
914                                             cfg,
915                                             uri,
916                                             filename,
917                                             0,
918                                             GNUNET_ECRS_uri_get_file_size
919                                             (uri), anonymityLevel, GNUNET_NO,
920                                             dpcb, dpcbClosure, tt, ttClosure);
921 }
922
923 /* end of download.c */