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