diff options
Diffstat (limited to 'src/fs/fs_download.c')
-rw-r--r-- | src/fs/fs_download.c | 2418 |
1 files changed, 0 insertions, 2418 deletions
diff --git a/src/fs/fs_download.c b/src/fs/fs_download.c deleted file mode 100644 index 5c98d224a..000000000 --- a/src/fs/fs_download.c +++ /dev/null | |||
@@ -1,2418 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2001-2012 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your 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 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file fs/fs_download.c | ||
22 | * @brief download methods | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "gnunet_constants.h" | ||
27 | #include "gnunet_fs_service.h" | ||
28 | #include "fs_api.h" | ||
29 | #include "fs_tree.h" | ||
30 | |||
31 | |||
32 | /** | ||
33 | * Determine if the given download (options and meta data) should cause | ||
34 | * use to try to do a recursive download. | ||
35 | */ | ||
36 | static int | ||
37 | is_recursive_download (struct GNUNET_FS_DownloadContext *dc) | ||
38 | { | ||
39 | return (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE)) && | ||
40 | ((GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (dc->meta)) || | ||
41 | ((NULL == dc->meta) && | ||
42 | ((NULL == dc->filename) || | ||
43 | ((strlen (dc->filename) >= strlen (GNUNET_FS_DIRECTORY_EXT)) && | ||
44 | (NULL != strstr (dc->filename + strlen (dc->filename) | ||
45 | - strlen (GNUNET_FS_DIRECTORY_EXT), | ||
46 | GNUNET_FS_DIRECTORY_EXT)))))); | ||
47 | } | ||
48 | |||
49 | |||
50 | /** | ||
51 | * We're storing the IBLOCKS after the DBLOCKS on disk (so that we | ||
52 | * only have to truncate the file once we're done). | ||
53 | * | ||
54 | * Given the offset of a block (with respect to the DBLOCKS) and its | ||
55 | * depth, return the offset where we would store this block in the | ||
56 | * file. | ||
57 | * | ||
58 | * @param fsize overall file size | ||
59 | * @param off offset of the block in the file | ||
60 | * @param depth depth of the block in the tree, 0 for DBLOCK | ||
61 | * @return off for DBLOCKS (depth == treedepth), | ||
62 | * otherwise an offset past the end | ||
63 | * of the file that does not overlap | ||
64 | * with the range for any other block | ||
65 | */ | ||
66 | static uint64_t | ||
67 | compute_disk_offset (uint64_t fsize, uint64_t off, unsigned int depth) | ||
68 | { | ||
69 | unsigned int i; | ||
70 | uint64_t lsize; /* what is the size of all IBlocks for depth "i"? */ | ||
71 | uint64_t loff; /* where do IBlocks for depth "i" start? */ | ||
72 | unsigned int ioff; /* which IBlock corresponds to "off" at depth "i"? */ | ||
73 | |||
74 | if (0 == depth) | ||
75 | return off; | ||
76 | /* first IBlocks start at the end of file, rounded up | ||
77 | * to full DBLOCK_SIZE */ | ||
78 | loff = ((fsize + DBLOCK_SIZE - 1) / DBLOCK_SIZE) * DBLOCK_SIZE; | ||
79 | lsize = | ||
80 | ((fsize + DBLOCK_SIZE - 1) / DBLOCK_SIZE) * sizeof(struct ContentHashKey); | ||
81 | GNUNET_assert (0 == (off % DBLOCK_SIZE)); | ||
82 | ioff = (off / DBLOCK_SIZE); | ||
83 | for (i = 1; i < depth; i++) | ||
84 | { | ||
85 | loff += lsize; | ||
86 | lsize = (lsize + CHK_PER_INODE - 1) / CHK_PER_INODE; | ||
87 | GNUNET_assert (lsize > 0); | ||
88 | GNUNET_assert (0 == (ioff % CHK_PER_INODE)); | ||
89 | ioff /= CHK_PER_INODE; | ||
90 | } | ||
91 | return loff + ioff * sizeof(struct ContentHashKey); | ||
92 | } | ||
93 | |||
94 | |||
95 | /** | ||
96 | * Fill in all of the generic fields for a download event and call the | ||
97 | * callback. | ||
98 | * | ||
99 | * @param pi structure to fill in | ||
100 | * @param dc overall download context | ||
101 | */ | ||
102 | void | ||
103 | GNUNET_FS_download_make_status_ (struct GNUNET_FS_ProgressInfo *pi, | ||
104 | struct GNUNET_FS_DownloadContext *dc) | ||
105 | { | ||
106 | pi->value.download.dc = dc; | ||
107 | pi->value.download.cctx = dc->client_info; | ||
108 | pi->value.download.pctx = | ||
109 | (NULL == dc->parent) ? NULL : dc->parent->client_info; | ||
110 | pi->value.download.sctx = | ||
111 | (NULL == dc->search) ? NULL : dc->search->client_info; | ||
112 | pi->value.download.uri = dc->uri; | ||
113 | pi->value.download.filename = dc->filename; | ||
114 | pi->value.download.size = dc->length; | ||
115 | /* FIXME: Fix duration calculation to account for pauses */ | ||
116 | pi->value.download.duration = | ||
117 | GNUNET_TIME_absolute_get_duration (dc->start_time); | ||
118 | pi->value.download.completed = dc->completed; | ||
119 | pi->value.download.anonymity = dc->anonymity; | ||
120 | pi->value.download.eta = | ||
121 | GNUNET_TIME_calculate_eta (dc->start_time, dc->completed, dc->length); | ||
122 | pi->value.download.is_active = (NULL == dc->mq) ? GNUNET_NO : GNUNET_YES; | ||
123 | pi->fsh = dc->h; | ||
124 | if (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE)) | ||
125 | dc->client_info = dc->h->upcb (dc->h->upcb_cls, pi); | ||
126 | else | ||
127 | dc->client_info = GNUNET_FS_search_probe_progress_ (NULL, pi); | ||
128 | } | ||
129 | |||
130 | |||
131 | /** | ||
132 | * Closure for iterator processing results. | ||
133 | */ | ||
134 | struct ProcessResultClosure | ||
135 | { | ||
136 | /** | ||
137 | * Hash of data. | ||
138 | */ | ||
139 | struct GNUNET_HashCode query; | ||
140 | |||
141 | /** | ||
142 | * Data found in P2P network. | ||
143 | */ | ||
144 | const void *data; | ||
145 | |||
146 | /** | ||
147 | * Our download context. | ||
148 | */ | ||
149 | struct GNUNET_FS_DownloadContext *dc; | ||
150 | |||
151 | /** | ||
152 | * When did we last transmit the request? | ||
153 | */ | ||
154 | struct GNUNET_TIME_Absolute last_transmission; | ||
155 | |||
156 | /** | ||
157 | * Number of bytes in data. | ||
158 | */ | ||
159 | size_t size; | ||
160 | |||
161 | /** | ||
162 | * Type of data. | ||
163 | */ | ||
164 | enum GNUNET_BLOCK_Type type; | ||
165 | |||
166 | /** | ||
167 | * Flag to indicate if this block should be stored on disk. | ||
168 | */ | ||
169 | int do_store; | ||
170 | |||
171 | /** | ||
172 | * how much respect did we offer to get this reply? | ||
173 | */ | ||
174 | uint32_t respect_offered; | ||
175 | |||
176 | /** | ||
177 | * how often did we transmit the query? | ||
178 | */ | ||
179 | uint32_t num_transmissions; | ||
180 | }; | ||
181 | |||
182 | |||
183 | /** | ||
184 | * Iterator over entries in the pending requests in the 'active' map for the | ||
185 | * reply that we just got. | ||
186 | * | ||
187 | * @param cls closure (our 'struct ProcessResultClosure') | ||
188 | * @param key query for the given value / request | ||
189 | * @param value value in the hash map (a 'struct DownloadRequest') | ||
190 | * @return #GNUNET_YES (we should continue to iterate); unless serious error | ||
191 | */ | ||
192 | static int | ||
193 | process_result_with_request (void *cls, | ||
194 | const struct GNUNET_HashCode *key, | ||
195 | void *value); | ||
196 | |||
197 | |||
198 | /** | ||
199 | * We've found a matching block without downloading it. | ||
200 | * Encrypt it and pass it to our "receive" function as | ||
201 | * if we had received it from the network. | ||
202 | * | ||
203 | * @param dc download in question | ||
204 | * @param chk request this relates to | ||
205 | * @param dr request details | ||
206 | * @param block plaintext data matching request | ||
207 | * @param len number of bytes in block | ||
208 | * @param do_store should we still store the block on disk? | ||
209 | * @return GNUNET_OK on success | ||
210 | */ | ||
211 | static int | ||
212 | encrypt_existing_match (struct GNUNET_FS_DownloadContext *dc, | ||
213 | const struct ContentHashKey *chk, | ||
214 | struct DownloadRequest *dr, | ||
215 | const char *block, | ||
216 | size_t len, | ||
217 | int do_store) | ||
218 | { | ||
219 | struct ProcessResultClosure prc; | ||
220 | char enc[len]; | ||
221 | struct GNUNET_CRYPTO_SymmetricSessionKey sk; | ||
222 | struct GNUNET_CRYPTO_SymmetricInitializationVector iv; | ||
223 | struct GNUNET_HashCode query; | ||
224 | |||
225 | GNUNET_CRYPTO_hash_to_aes_key (&chk->key, &sk, &iv); | ||
226 | if (-1 == GNUNET_CRYPTO_symmetric_encrypt (block, len, &sk, &iv, enc)) | ||
227 | { | ||
228 | GNUNET_break (0); | ||
229 | return GNUNET_SYSERR; | ||
230 | } | ||
231 | GNUNET_CRYPTO_hash (enc, len, &query); | ||
232 | if (0 != memcmp (&query, &chk->query, sizeof(struct GNUNET_HashCode))) | ||
233 | { | ||
234 | GNUNET_break_op (0); | ||
235 | return GNUNET_SYSERR; | ||
236 | } | ||
237 | GNUNET_log ( | ||
238 | GNUNET_ERROR_TYPE_DEBUG, | ||
239 | "Matching %u byte block for `%s' at offset %llu already present, no need for download!\n", | ||
240 | (unsigned int) len, | ||
241 | dc->filename, | ||
242 | (unsigned long long) dr->offset); | ||
243 | /* already got it! */ | ||
244 | prc.dc = dc; | ||
245 | prc.data = enc; | ||
246 | prc.size = len; | ||
247 | prc.type = (0 == dr->depth) ? GNUNET_BLOCK_TYPE_FS_DBLOCK | ||
248 | : GNUNET_BLOCK_TYPE_FS_IBLOCK; | ||
249 | prc.query = chk->query; | ||
250 | prc.do_store = do_store; | ||
251 | prc.last_transmission = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
252 | process_result_with_request (&prc, &chk->key, dr); | ||
253 | return GNUNET_OK; | ||
254 | } | ||
255 | |||
256 | |||
257 | /** | ||
258 | * We've lost our connection with the FS service. | ||
259 | * Re-establish it and re-transmit all of our | ||
260 | * pending requests. | ||
261 | * | ||
262 | * @param dc download context that is having trouble | ||
263 | */ | ||
264 | static void | ||
265 | try_reconnect (struct GNUNET_FS_DownloadContext *dc); | ||
266 | |||
267 | |||
268 | /** | ||
269 | * We found an entry in a directory. Check if the respective child | ||
270 | * already exists and if not create the respective child download. | ||
271 | * | ||
272 | * @param cls the parent download | ||
273 | * @param filename name of the file in the directory | ||
274 | * @param uri URI of the file (CHK or LOC) | ||
275 | * @param meta meta data of the file | ||
276 | * @param length number of bytes in data | ||
277 | * @param data contents of the file (or NULL if they were not inlined) | ||
278 | */ | ||
279 | static void | ||
280 | trigger_recursive_download (void *cls, | ||
281 | const char *filename, | ||
282 | const struct GNUNET_FS_Uri *uri, | ||
283 | const struct GNUNET_CONTAINER_MetaData *meta, | ||
284 | size_t length, | ||
285 | const void *data); | ||
286 | |||
287 | |||
288 | /** | ||
289 | * We're done downloading a directory. Open the file and | ||
290 | * trigger all of the (remaining) child downloads. | ||
291 | * | ||
292 | * @param dc context of download that just completed | ||
293 | */ | ||
294 | static void | ||
295 | full_recursive_download (struct GNUNET_FS_DownloadContext *dc) | ||
296 | { | ||
297 | size_t size; | ||
298 | uint64_t size64; | ||
299 | void *data; | ||
300 | struct GNUNET_DISK_FileHandle *h; | ||
301 | struct GNUNET_DISK_MapHandle *m; | ||
302 | |||
303 | size64 = GNUNET_FS_uri_chk_get_file_size (dc->uri); | ||
304 | size = (size_t) size64; | ||
305 | if (size64 != (uint64_t) size) | ||
306 | { | ||
307 | GNUNET_log ( | ||
308 | GNUNET_ERROR_TYPE_ERROR, | ||
309 | _ ( | ||
310 | "Recursive downloads of directories larger than 4 GB are not supported on 32-bit systems\n")); | ||
311 | return; | ||
312 | } | ||
313 | if (NULL != dc->filename) | ||
314 | { | ||
315 | h = GNUNET_DISK_file_open (dc->filename, | ||
316 | GNUNET_DISK_OPEN_READ, | ||
317 | GNUNET_DISK_PERM_NONE); | ||
318 | } | ||
319 | else | ||
320 | { | ||
321 | GNUNET_assert (NULL != dc->temp_filename); | ||
322 | h = GNUNET_DISK_file_open (dc->temp_filename, | ||
323 | GNUNET_DISK_OPEN_READ, | ||
324 | GNUNET_DISK_PERM_NONE); | ||
325 | } | ||
326 | if (NULL == h) | ||
327 | return; /* oops */ | ||
328 | data = GNUNET_DISK_file_map (h, &m, GNUNET_DISK_MAP_TYPE_READ, size); | ||
329 | if (NULL == data) | ||
330 | { | ||
331 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
332 | _ ("Directory too large for system address space\n")); | ||
333 | } | ||
334 | else | ||
335 | { | ||
336 | if (GNUNET_OK != | ||
337 | GNUNET_FS_directory_list_contents (size, | ||
338 | data, | ||
339 | 0, | ||
340 | &trigger_recursive_download, | ||
341 | dc)) | ||
342 | { | ||
343 | GNUNET_log ( | ||
344 | GNUNET_ERROR_TYPE_WARNING, | ||
345 | _ ( | ||
346 | "Failed to access full directory contents of `%s' for recursive download\n"), | ||
347 | dc->filename); | ||
348 | } | ||
349 | GNUNET_DISK_file_unmap (m); | ||
350 | } | ||
351 | GNUNET_DISK_file_close (h); | ||
352 | if (NULL == dc->filename) | ||
353 | { | ||
354 | if (0 != unlink (dc->temp_filename)) | ||
355 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
356 | "unlink", | ||
357 | dc->temp_filename); | ||
358 | GNUNET_free (dc->temp_filename); | ||
359 | dc->temp_filename = NULL; | ||
360 | } | ||
361 | } | ||
362 | |||
363 | |||
364 | /** | ||
365 | * Check if all child-downloads have completed (or trigger them if | ||
366 | * necessary) and once we're completely done, signal completion (and | ||
367 | * possibly recurse to parent). This function MUST be called when the | ||
368 | * download of a file itself is done or when the download of a file is | ||
369 | * done and then later a direct child download has completed (and | ||
370 | * hence this download may complete itself). | ||
371 | * | ||
372 | * @param dc download to check for completion of children | ||
373 | */ | ||
374 | static void | ||
375 | check_completed (struct GNUNET_FS_DownloadContext *dc) | ||
376 | { | ||
377 | struct GNUNET_FS_ProgressInfo pi; | ||
378 | struct GNUNET_FS_DownloadContext *pos; | ||
379 | |||
380 | /* first, check if we need to download children */ | ||
381 | if (is_recursive_download (dc)) | ||
382 | full_recursive_download (dc); | ||
383 | /* then, check if children are done already */ | ||
384 | for (pos = dc->child_head; NULL != pos; pos = pos->next) | ||
385 | { | ||
386 | if ((NULL == pos->emsg) && (pos->completed < pos->length)) | ||
387 | return; /* not done yet */ | ||
388 | if ((NULL != pos->child_head) && (pos->has_finished != GNUNET_YES)) | ||
389 | return; /* not transitively done yet */ | ||
390 | } | ||
391 | /* All of our children are done, so mark this download done */ | ||
392 | dc->has_finished = GNUNET_YES; | ||
393 | if (NULL != dc->job_queue) | ||
394 | { | ||
395 | GNUNET_FS_dequeue_ (dc->job_queue); | ||
396 | dc->job_queue = NULL; | ||
397 | } | ||
398 | if (NULL != dc->task) | ||
399 | { | ||
400 | GNUNET_SCHEDULER_cancel (dc->task); | ||
401 | dc->task = NULL; | ||
402 | } | ||
403 | if (NULL != dc->rfh) | ||
404 | { | ||
405 | GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (dc->rfh)); | ||
406 | dc->rfh = NULL; | ||
407 | } | ||
408 | GNUNET_FS_download_sync_ (dc); | ||
409 | |||
410 | /* signal completion */ | ||
411 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_COMPLETED; | ||
412 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
413 | |||
414 | /* let parent know */ | ||
415 | if (NULL != dc->parent) | ||
416 | check_completed (dc->parent); | ||
417 | } | ||
418 | |||
419 | |||
420 | /** | ||
421 | * We got a block of plaintext data (from the meta data). | ||
422 | * Try it for upward reconstruction of the data. On success, | ||
423 | * the top-level block will move to state BRS_DOWNLOAD_UP. | ||
424 | * | ||
425 | * @param dc context for the download | ||
426 | * @param dr download request to match against | ||
427 | * @param data plaintext data, starting from the beginning of the file | ||
428 | * @param data_len number of bytes in data | ||
429 | */ | ||
430 | static void | ||
431 | try_match_block (struct GNUNET_FS_DownloadContext *dc, | ||
432 | struct DownloadRequest *dr, | ||
433 | const char *data, | ||
434 | size_t data_len) | ||
435 | { | ||
436 | struct GNUNET_FS_ProgressInfo pi; | ||
437 | unsigned int i; | ||
438 | char enc[DBLOCK_SIZE]; | ||
439 | struct ContentHashKey chks[CHK_PER_INODE]; | ||
440 | struct ContentHashKey in_chk; | ||
441 | struct GNUNET_CRYPTO_SymmetricSessionKey sk; | ||
442 | struct GNUNET_CRYPTO_SymmetricInitializationVector iv; | ||
443 | size_t dlen; | ||
444 | struct DownloadRequest *drc; | ||
445 | struct GNUNET_DISK_FileHandle *fh; | ||
446 | int complete; | ||
447 | const char *fn; | ||
448 | const char *odata; | ||
449 | size_t odata_len; | ||
450 | |||
451 | odata = data; | ||
452 | odata_len = data_len; | ||
453 | if (BRS_DOWNLOAD_UP == dr->state) | ||
454 | return; | ||
455 | if (dr->depth > 0) | ||
456 | { | ||
457 | if ((dc->offset > 0) || | ||
458 | (dc->length < GNUNET_ntohll (dc->uri->data.chk.file_length))) | ||
459 | { | ||
460 | /* NOTE: this test is not tight, but should suffice; the issue | ||
461 | here is that 'dr->num_children' may inherently only specify a | ||
462 | smaller range than what is in the original file; | ||
463 | thus, reconstruction of (some) inner blocks will fail. | ||
464 | FIXME: we might eventually want to write a tighter test to | ||
465 | maximize the circumstances under which we do succeed with | ||
466 | IBlock reconstruction. (need good tests though). */return; | ||
467 | } | ||
468 | complete = GNUNET_YES; | ||
469 | for (i = 0; i < dr->num_children; i++) | ||
470 | { | ||
471 | drc = dr->children[i]; | ||
472 | try_match_block (dc, drc, data, data_len); | ||
473 | if (drc->state != BRS_RECONSTRUCT_META_UP) | ||
474 | complete = GNUNET_NO; | ||
475 | else | ||
476 | chks[i] = drc->chk; | ||
477 | } | ||
478 | if (GNUNET_YES != complete) | ||
479 | return; | ||
480 | data = (const char *) chks; | ||
481 | dlen = dr->num_children * sizeof(struct ContentHashKey); | ||
482 | } | ||
483 | else | ||
484 | { | ||
485 | if (dr->offset > data_len) | ||
486 | return; /* oops */ | ||
487 | dlen = GNUNET_MIN (data_len - dr->offset, DBLOCK_SIZE); | ||
488 | } | ||
489 | GNUNET_CRYPTO_hash (&data[dr->offset], dlen, &in_chk.key); | ||
490 | GNUNET_CRYPTO_hash_to_aes_key (&in_chk.key, &sk, &iv); | ||
491 | if (-1 == | ||
492 | GNUNET_CRYPTO_symmetric_encrypt (&data[dr->offset], dlen, &sk, &iv, enc)) | ||
493 | { | ||
494 | GNUNET_break (0); | ||
495 | return; | ||
496 | } | ||
497 | GNUNET_CRYPTO_hash (enc, dlen, &in_chk.query); | ||
498 | switch (dr->state) | ||
499 | { | ||
500 | case BRS_INIT: | ||
501 | dr->chk = in_chk; | ||
502 | dr->state = BRS_RECONSTRUCT_META_UP; | ||
503 | break; | ||
504 | |||
505 | case BRS_CHK_SET: | ||
506 | if (0 != memcmp (&in_chk, &dr->chk, sizeof(struct ContentHashKey))) | ||
507 | { | ||
508 | /* other peer provided bogus meta data */ | ||
509 | GNUNET_break_op (0); | ||
510 | break; | ||
511 | } | ||
512 | /* write block to disk */ | ||
513 | fn = (NULL != dc->filename) ? dc->filename : dc->temp_filename; | ||
514 | if (NULL != fn) | ||
515 | { | ||
516 | fh = GNUNET_DISK_file_open (fn, | ||
517 | GNUNET_DISK_OPEN_READWRITE | ||
518 | | GNUNET_DISK_OPEN_CREATE | ||
519 | | GNUNET_DISK_OPEN_TRUNCATE, | ||
520 | GNUNET_DISK_PERM_USER_READ | ||
521 | | GNUNET_DISK_PERM_USER_WRITE | ||
522 | | GNUNET_DISK_PERM_GROUP_READ | ||
523 | | GNUNET_DISK_PERM_OTHER_READ); | ||
524 | if (NULL == fh) | ||
525 | { | ||
526 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", fn); | ||
527 | GNUNET_asprintf (&dc->emsg, | ||
528 | _ ("Failed to open file `%s' for writing"), | ||
529 | fn); | ||
530 | GNUNET_DISK_file_close (fh); | ||
531 | dr->state = BRS_ERROR; | ||
532 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR; | ||
533 | pi.value.download.specifics.error.message = dc->emsg; | ||
534 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
535 | return; | ||
536 | } | ||
537 | if (data_len != GNUNET_DISK_file_write (fh, odata, odata_len)) | ||
538 | { | ||
539 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "write", fn); | ||
540 | GNUNET_asprintf (&dc->emsg, | ||
541 | _ ("Failed to open file `%s' for writing"), | ||
542 | fn); | ||
543 | GNUNET_DISK_file_close (fh); | ||
544 | dr->state = BRS_ERROR; | ||
545 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR; | ||
546 | pi.value.download.specifics.error.message = dc->emsg; | ||
547 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
548 | return; | ||
549 | } | ||
550 | GNUNET_DISK_file_close (fh); | ||
551 | } | ||
552 | /* signal success */ | ||
553 | dr->state = BRS_DOWNLOAD_UP; | ||
554 | dc->completed = dc->length; | ||
555 | GNUNET_FS_download_sync_ (dc); | ||
556 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS; | ||
557 | pi.value.download.specifics.progress.data = data; | ||
558 | pi.value.download.specifics.progress.offset = 0; | ||
559 | pi.value.download.specifics.progress.data_len = dlen; | ||
560 | pi.value.download.specifics.progress.depth = 0; | ||
561 | pi.value.download.specifics.progress.respect_offered = 0; | ||
562 | pi.value.download.specifics.progress.block_download_duration = | ||
563 | GNUNET_TIME_UNIT_ZERO; | ||
564 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
565 | if ((NULL != dc->filename) && | ||
566 | (0 != truncate (dc->filename, | ||
567 | GNUNET_ntohll (dc->uri->data.chk.file_length)))) | ||
568 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
569 | "truncate", | ||
570 | dc->filename); | ||
571 | check_completed (dc); | ||
572 | break; | ||
573 | |||
574 | default: | ||
575 | /* how did we get here? */ | ||
576 | GNUNET_break (0); | ||
577 | break; | ||
578 | } | ||
579 | } | ||
580 | |||
581 | |||
582 | /** | ||
583 | * Type of a function that libextractor calls for each | ||
584 | * meta data item found. If we find full data meta data, | ||
585 | * call 'try_match_block' on it. | ||
586 | * | ||
587 | * @param cls our 'struct GNUNET_FS_DownloadContext*' | ||
588 | * @param plugin_name name of the plugin that produced this value; | ||
589 | * special values can be used (e.g. '<zlib>' for zlib being | ||
590 | * used in the main libextractor library and yielding | ||
591 | * meta data). | ||
592 | * @param type libextractor-type describing the meta data | ||
593 | * @param format basic format information about data | ||
594 | * @param data_mime_type mime-type of data (not of the original file); | ||
595 | * can be NULL (if mime-type is not known) | ||
596 | * @param data actual meta-data found | ||
597 | * @param data_len number of bytes in data | ||
598 | * @return 0 to continue extracting, 1 to abort | ||
599 | */ | ||
600 | static int | ||
601 | match_full_data (void *cls, | ||
602 | const char *plugin_name, | ||
603 | enum EXTRACTOR_MetaType type, | ||
604 | enum EXTRACTOR_MetaFormat format, | ||
605 | const char *data_mime_type, | ||
606 | const char *data, | ||
607 | size_t data_len) | ||
608 | { | ||
609 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
610 | |||
611 | if (EXTRACTOR_METATYPE_GNUNET_FULL_DATA != type) | ||
612 | return 0; | ||
613 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
614 | "Found %u bytes of FD!\n", | ||
615 | (unsigned int) data_len); | ||
616 | if (GNUNET_FS_uri_chk_get_file_size (dc->uri) != data_len) | ||
617 | { | ||
618 | GNUNET_break_op (0); | ||
619 | return 1; /* bogus meta data */ | ||
620 | } | ||
621 | try_match_block (dc, dc->top_request, data, data_len); | ||
622 | return 1; | ||
623 | } | ||
624 | |||
625 | |||
626 | /** | ||
627 | * Set the state of the given download request to | ||
628 | * BRS_DOWNLOAD_UP and propagate it up the tree. | ||
629 | * | ||
630 | * @param dr download request that is done | ||
631 | */ | ||
632 | static void | ||
633 | propagate_up (struct DownloadRequest *dr) | ||
634 | { | ||
635 | unsigned int i; | ||
636 | |||
637 | do | ||
638 | { | ||
639 | dr->state = BRS_DOWNLOAD_UP; | ||
640 | dr = dr->parent; | ||
641 | if (NULL == dr) | ||
642 | break; | ||
643 | for (i = 0; i < dr->num_children; i++) | ||
644 | if (dr->children[i]->state != BRS_DOWNLOAD_UP) | ||
645 | break; | ||
646 | } | ||
647 | while (i == dr->num_children); | ||
648 | } | ||
649 | |||
650 | |||
651 | /** | ||
652 | * Try top-down reconstruction. Before, the given request node | ||
653 | * must have the state BRS_CHK_SET. Afterwards, more nodes may | ||
654 | * have that state or advanced to BRS_DOWNLOAD_DOWN or even | ||
655 | * BRS_DOWNLOAD_UP. It is also possible to get BRS_ERROR on the | ||
656 | * top level. | ||
657 | * | ||
658 | * @param dc overall download this block belongs to | ||
659 | * @param dr block to reconstruct | ||
660 | */ | ||
661 | static void | ||
662 | try_top_down_reconstruction (struct GNUNET_FS_DownloadContext *dc, | ||
663 | struct DownloadRequest *dr) | ||
664 | { | ||
665 | uint64_t off; | ||
666 | char block[DBLOCK_SIZE]; | ||
667 | struct GNUNET_HashCode key; | ||
668 | uint64_t total; | ||
669 | size_t len; | ||
670 | unsigned int i; | ||
671 | struct DownloadRequest *drc; | ||
672 | uint64_t child_block_size; | ||
673 | const struct ContentHashKey *chks; | ||
674 | int up_done; | ||
675 | |||
676 | GNUNET_assert (NULL != dc->rfh); | ||
677 | GNUNET_assert (BRS_CHK_SET == dr->state); | ||
678 | total = GNUNET_FS_uri_chk_get_file_size (dc->uri); | ||
679 | GNUNET_assert (dr->depth < dc->treedepth); | ||
680 | len = GNUNET_FS_tree_calculate_block_size (total, dr->offset, dr->depth); | ||
681 | GNUNET_assert (len <= DBLOCK_SIZE); | ||
682 | off = compute_disk_offset (total, dr->offset, dr->depth); | ||
683 | if (dc->old_file_size < off + len) | ||
684 | return; /* failure */ | ||
685 | if (off != GNUNET_DISK_file_seek (dc->rfh, off, GNUNET_DISK_SEEK_SET)) | ||
686 | { | ||
687 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "seek", dc->filename); | ||
688 | return; /* failure */ | ||
689 | } | ||
690 | if (len != GNUNET_DISK_file_read (dc->rfh, block, len)) | ||
691 | { | ||
692 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "read", dc->filename); | ||
693 | return; /* failure */ | ||
694 | } | ||
695 | GNUNET_CRYPTO_hash (block, len, &key); | ||
696 | if (0 != memcmp (&key, &dr->chk.key, sizeof(struct GNUNET_HashCode))) | ||
697 | return; /* mismatch */ | ||
698 | if (GNUNET_OK != | ||
699 | encrypt_existing_match (dc, &dr->chk, dr, block, len, GNUNET_NO)) | ||
700 | { | ||
701 | /* hash matches but encrypted block does not, really bad */ | ||
702 | dr->state = BRS_ERROR; | ||
703 | /* propagate up */ | ||
704 | while (NULL != dr->parent) | ||
705 | { | ||
706 | dr = dr->parent; | ||
707 | dr->state = BRS_ERROR; | ||
708 | } | ||
709 | return; | ||
710 | } | ||
711 | /* block matches */ | ||
712 | dr->state = BRS_DOWNLOAD_DOWN; | ||
713 | |||
714 | /* set CHKs for children */ | ||
715 | up_done = GNUNET_YES; | ||
716 | chks = (const struct ContentHashKey *) block; | ||
717 | for (i = 0; i < dr->num_children; i++) | ||
718 | { | ||
719 | drc = dr->children[i]; | ||
720 | GNUNET_assert (drc->offset >= dr->offset); | ||
721 | child_block_size = GNUNET_FS_tree_compute_tree_size (drc->depth); | ||
722 | GNUNET_assert (0 == (drc->offset - dr->offset) % child_block_size); | ||
723 | if (BRS_INIT == drc->state) | ||
724 | { | ||
725 | drc->state = BRS_CHK_SET; | ||
726 | drc->chk = chks[drc->chk_idx]; | ||
727 | try_top_down_reconstruction (dc, drc); | ||
728 | } | ||
729 | if (BRS_DOWNLOAD_UP != drc->state) | ||
730 | up_done = GNUNET_NO; /* children not all done */ | ||
731 | } | ||
732 | if (GNUNET_YES == up_done) | ||
733 | propagate_up (dr); /* children all done (or no children...) */ | ||
734 | } | ||
735 | |||
736 | |||
737 | /** | ||
738 | * Add entries to the message queue. | ||
739 | * | ||
740 | * @param cls our download context | ||
741 | * @param key unused | ||
742 | * @param entry entry of type `struct DownloadRequest` | ||
743 | * @return #GNUNET_OK | ||
744 | */ | ||
745 | static int | ||
746 | retry_entry (void *cls, const struct GNUNET_HashCode *key, void *entry) | ||
747 | { | ||
748 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
749 | struct DownloadRequest *dr = entry; | ||
750 | struct SearchMessage *sm; | ||
751 | struct GNUNET_MQ_Envelope *env; | ||
752 | |||
753 | env = GNUNET_MQ_msg (sm, GNUNET_MESSAGE_TYPE_FS_START_SEARCH); | ||
754 | if (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY)) | ||
755 | sm->options = htonl (GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY); | ||
756 | else | ||
757 | sm->options = htonl (GNUNET_FS_SEARCH_OPTION_NONE); | ||
758 | if (0 == dr->depth) | ||
759 | sm->type = htonl (GNUNET_BLOCK_TYPE_FS_DBLOCK); | ||
760 | else | ||
761 | sm->type = htonl (GNUNET_BLOCK_TYPE_FS_IBLOCK); | ||
762 | sm->anonymity_level = htonl (dc->anonymity); | ||
763 | sm->target = dc->target; | ||
764 | sm->query = dr->chk.query; | ||
765 | GNUNET_MQ_send (dc->mq, env); | ||
766 | return GNUNET_OK; | ||
767 | } | ||
768 | |||
769 | |||
770 | /** | ||
771 | * Schedule the download of the specified block in the tree. | ||
772 | * | ||
773 | * @param dc overall download this block belongs to | ||
774 | * @param dr request to schedule | ||
775 | */ | ||
776 | static void | ||
777 | schedule_block_download (struct GNUNET_FS_DownloadContext *dc, | ||
778 | struct DownloadRequest *dr) | ||
779 | { | ||
780 | unsigned int i; | ||
781 | |||
782 | switch (dr->state) | ||
783 | { | ||
784 | case BRS_INIT: | ||
785 | GNUNET_assert (0); | ||
786 | break; | ||
787 | |||
788 | case BRS_RECONSTRUCT_DOWN: | ||
789 | GNUNET_assert (0); | ||
790 | break; | ||
791 | |||
792 | case BRS_RECONSTRUCT_META_UP: | ||
793 | GNUNET_assert (0); | ||
794 | break; | ||
795 | |||
796 | case BRS_RECONSTRUCT_UP: | ||
797 | GNUNET_assert (0); | ||
798 | break; | ||
799 | |||
800 | case BRS_CHK_SET: | ||
801 | /* normal case, start download */ | ||
802 | break; | ||
803 | |||
804 | case BRS_DOWNLOAD_DOWN: | ||
805 | for (i = 0; i < dr->num_children; i++) | ||
806 | schedule_block_download (dc, dr->children[i]); | ||
807 | return; | ||
808 | |||
809 | case BRS_DOWNLOAD_UP: | ||
810 | /* We're done! */ | ||
811 | return; | ||
812 | |||
813 | case BRS_ERROR: | ||
814 | GNUNET_break (0); | ||
815 | return; | ||
816 | } | ||
817 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
818 | "Scheduling download at offset %llu and depth %u for `%s'\n", | ||
819 | (unsigned long long) dr->offset, | ||
820 | dr->depth, | ||
821 | GNUNET_h2s (&dr->chk.query)); | ||
822 | if (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains_value (dc->active, | ||
823 | &dr->chk.query, | ||
824 | dr)) | ||
825 | return; /* already active */ | ||
826 | GNUNET_CONTAINER_multihashmap_put (dc->active, | ||
827 | &dr->chk.query, | ||
828 | dr, | ||
829 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
830 | if (NULL == dc->mq) | ||
831 | return; /* download not active */ | ||
832 | retry_entry (dc, &dr->chk.query, dr); | ||
833 | } | ||
834 | |||
835 | |||
836 | #define GNUNET_FS_URI_CHK_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_CHK_INFIX | ||
837 | |||
838 | /** | ||
839 | * We found an entry in a directory. Check if the respective child | ||
840 | * already exists and if not create the respective child download. | ||
841 | * | ||
842 | * @param cls the parent download | ||
843 | * @param filename name of the file in the directory | ||
844 | * @param uri URI of the file (CHK or LOC) | ||
845 | * @param meta meta data of the file | ||
846 | * @param length number of bytes in data | ||
847 | * @param data contents of the file (or NULL if they were not inlined) | ||
848 | */ | ||
849 | static void | ||
850 | trigger_recursive_download (void *cls, | ||
851 | const char *filename, | ||
852 | const struct GNUNET_FS_Uri *uri, | ||
853 | const struct GNUNET_CONTAINER_MetaData *meta, | ||
854 | size_t length, | ||
855 | const void *data) | ||
856 | { | ||
857 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
858 | struct GNUNET_FS_DownloadContext *cpos; | ||
859 | char *temp_name; | ||
860 | char *fn; | ||
861 | char *us; | ||
862 | char *ext; | ||
863 | char *dn; | ||
864 | char *pos; | ||
865 | char *full_name; | ||
866 | char *sfn; | ||
867 | |||
868 | if (NULL == uri) | ||
869 | return; /* entry for the directory itself */ | ||
870 | cpos = dc->child_head; | ||
871 | while (NULL != cpos) | ||
872 | { | ||
873 | if ((GNUNET_FS_uri_test_equal (uri, cpos->uri)) || | ||
874 | ((NULL != filename) && (0 == strcmp (cpos->filename, filename)))) | ||
875 | break; | ||
876 | cpos = cpos->next; | ||
877 | } | ||
878 | if (NULL != cpos) | ||
879 | return; /* already exists */ | ||
880 | fn = NULL; | ||
881 | if (NULL == filename) | ||
882 | { | ||
883 | fn = GNUNET_FS_meta_data_suggest_filename (meta); | ||
884 | if (NULL == fn) | ||
885 | { | ||
886 | us = GNUNET_FS_uri_to_string (uri); | ||
887 | fn = GNUNET_strdup (&us[strlen (GNUNET_FS_URI_CHK_PREFIX)]); | ||
888 | GNUNET_free (us); | ||
889 | } | ||
890 | else if ('.' == fn[0]) | ||
891 | { | ||
892 | ext = fn; | ||
893 | us = GNUNET_FS_uri_to_string (uri); | ||
894 | GNUNET_asprintf (&fn, | ||
895 | "%s%s", | ||
896 | &us[strlen (GNUNET_FS_URI_CHK_PREFIX)], | ||
897 | ext); | ||
898 | GNUNET_free (ext); | ||
899 | GNUNET_free (us); | ||
900 | } | ||
901 | /* change '\' to '/' (this should have happened | ||
902 | * during insertion, but malicious peers may | ||
903 | * not have done this) */ | ||
904 | while (NULL != (pos = strstr (fn, "\\"))) | ||
905 | *pos = '/'; | ||
906 | /* remove '../' everywhere (again, well-behaved | ||
907 | * peers don't do this, but don't trust that | ||
908 | * we did not get something nasty) */ | ||
909 | while (NULL != (pos = strstr (fn, "../"))) | ||
910 | { | ||
911 | pos[0] = '_'; | ||
912 | pos[1] = '_'; | ||
913 | pos[2] = '_'; | ||
914 | } | ||
915 | filename = fn; | ||
916 | } | ||
917 | if (NULL == dc->filename) | ||
918 | { | ||
919 | full_name = NULL; | ||
920 | } | ||
921 | else | ||
922 | { | ||
923 | dn = GNUNET_strdup (dc->filename); | ||
924 | GNUNET_break ( | ||
925 | (strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) && | ||
926 | (NULL != strstr (dn + strlen (dn) - strlen (GNUNET_FS_DIRECTORY_EXT), | ||
927 | GNUNET_FS_DIRECTORY_EXT))); | ||
928 | sfn = GNUNET_strdup (filename); | ||
929 | while ((strlen (sfn) > 0) && ('/' == filename[strlen (sfn) - 1])) | ||
930 | sfn[strlen (sfn) - 1] = '\0'; | ||
931 | if ((strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) && | ||
932 | (NULL != strstr (dn + strlen (dn) - strlen (GNUNET_FS_DIRECTORY_EXT), | ||
933 | GNUNET_FS_DIRECTORY_EXT))) | ||
934 | dn[strlen (dn) - strlen (GNUNET_FS_DIRECTORY_EXT)] = '\0'; | ||
935 | if ((GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta)) && | ||
936 | ((strlen (filename) < strlen (GNUNET_FS_DIRECTORY_EXT)) || | ||
937 | (NULL == strstr (filename + strlen (filename) | ||
938 | - strlen (GNUNET_FS_DIRECTORY_EXT), | ||
939 | GNUNET_FS_DIRECTORY_EXT)))) | ||
940 | { | ||
941 | GNUNET_asprintf (&full_name, | ||
942 | "%s%s%s%s", | ||
943 | dn, | ||
944 | DIR_SEPARATOR_STR, | ||
945 | sfn, | ||
946 | GNUNET_FS_DIRECTORY_EXT); | ||
947 | } | ||
948 | else | ||
949 | { | ||
950 | GNUNET_asprintf (&full_name, "%s%s%s", dn, DIR_SEPARATOR_STR, sfn); | ||
951 | } | ||
952 | GNUNET_free (sfn); | ||
953 | GNUNET_free (dn); | ||
954 | } | ||
955 | if ((NULL != full_name) && | ||
956 | (GNUNET_OK != GNUNET_DISK_directory_create_for_file (full_name))) | ||
957 | { | ||
958 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
959 | _ ( | ||
960 | "Failed to create directory for recursive download of `%s'\n"), | ||
961 | full_name); | ||
962 | GNUNET_free (full_name); | ||
963 | GNUNET_free (fn); | ||
964 | return; | ||
965 | } | ||
966 | |||
967 | temp_name = NULL; | ||
968 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
969 | "Triggering recursive download of size %llu with %u bytes MD\n", | ||
970 | (unsigned long long) GNUNET_FS_uri_chk_get_file_size (uri), | ||
971 | (unsigned int) GNUNET_CONTAINER_meta_data_get_serialized_size ( | ||
972 | meta)); | ||
973 | GNUNET_FS_download_start (dc->h, | ||
974 | uri, | ||
975 | meta, | ||
976 | full_name, | ||
977 | temp_name, | ||
978 | 0, | ||
979 | GNUNET_FS_uri_chk_get_file_size (uri), | ||
980 | dc->anonymity, | ||
981 | dc->options, | ||
982 | NULL, | ||
983 | dc); | ||
984 | GNUNET_free (full_name); | ||
985 | GNUNET_free (temp_name); | ||
986 | GNUNET_free (fn); | ||
987 | } | ||
988 | |||
989 | |||
990 | /** | ||
991 | * (recursively) free download request structure | ||
992 | * | ||
993 | * @param dr request to free | ||
994 | */ | ||
995 | void | ||
996 | GNUNET_FS_free_download_request_ (struct DownloadRequest *dr) | ||
997 | { | ||
998 | if (NULL == dr) | ||
999 | return; | ||
1000 | for (unsigned int i = 0; i < dr->num_children; i++) | ||
1001 | GNUNET_FS_free_download_request_ (dr->children[i]); | ||
1002 | GNUNET_free (dr->children); | ||
1003 | GNUNET_free (dr); | ||
1004 | } | ||
1005 | |||
1006 | |||
1007 | /** | ||
1008 | * Iterator over entries in the pending requests in the 'active' map for the | ||
1009 | * reply that we just got. | ||
1010 | * | ||
1011 | * @param cls closure (our `struct ProcessResultClosure`) | ||
1012 | * @param key query for the given value / request | ||
1013 | * @param value value in the hash map (a `struct DownloadRequest`) | ||
1014 | * @return #GNUNET_YES (we should continue to iterate); unless serious error | ||
1015 | */ | ||
1016 | static int | ||
1017 | process_result_with_request (void *cls, | ||
1018 | const struct GNUNET_HashCode *key, | ||
1019 | void *value) | ||
1020 | { | ||
1021 | struct ProcessResultClosure *prc = cls; | ||
1022 | struct DownloadRequest *dr = value; | ||
1023 | struct GNUNET_FS_DownloadContext *dc = prc->dc; | ||
1024 | struct DownloadRequest *drc; | ||
1025 | struct GNUNET_DISK_FileHandle *fh = NULL; | ||
1026 | struct GNUNET_CRYPTO_SymmetricSessionKey skey; | ||
1027 | struct GNUNET_CRYPTO_SymmetricInitializationVector iv; | ||
1028 | char pt[prc->size]; | ||
1029 | struct GNUNET_FS_ProgressInfo pi; | ||
1030 | uint64_t off; | ||
1031 | size_t bs; | ||
1032 | size_t app; | ||
1033 | int i; | ||
1034 | struct ContentHashKey *chkarr; | ||
1035 | |||
1036 | GNUNET_log ( | ||
1037 | GNUNET_ERROR_TYPE_DEBUG, | ||
1038 | "Received %u byte block `%s' matching pending request at depth %u and offset %llu/%llu\n", | ||
1039 | (unsigned int) prc->size, | ||
1040 | GNUNET_h2s (key), | ||
1041 | dr->depth, | ||
1042 | (unsigned long long) dr->offset, | ||
1043 | (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length)); | ||
1044 | bs = GNUNET_FS_tree_calculate_block_size (GNUNET_ntohll ( | ||
1045 | dc->uri->data.chk.file_length), | ||
1046 | dr->offset, | ||
1047 | dr->depth); | ||
1048 | if (prc->size != bs) | ||
1049 | { | ||
1050 | GNUNET_asprintf ( | ||
1051 | &dc->emsg, | ||
1052 | _ ( | ||
1053 | "Internal error or bogus download URI (expected %lu bytes at depth %u and offset %llu/%llu, got %lu bytes)"), | ||
1054 | bs, | ||
1055 | dr->depth, | ||
1056 | (unsigned long long) dr->offset, | ||
1057 | (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length), | ||
1058 | prc->size); | ||
1059 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%s\n", dc->emsg); | ||
1060 | while (NULL != dr->parent) | ||
1061 | { | ||
1062 | dr->state = BRS_ERROR; | ||
1063 | dr = dr->parent; | ||
1064 | } | ||
1065 | dr->state = BRS_ERROR; | ||
1066 | goto signal_error; | ||
1067 | } | ||
1068 | |||
1069 | (void) GNUNET_CONTAINER_multihashmap_remove (dc->active, &prc->query, dr); | ||
1070 | GNUNET_CRYPTO_hash_to_aes_key (&dr->chk.key, &skey, &iv); | ||
1071 | if (-1 == | ||
1072 | GNUNET_CRYPTO_symmetric_decrypt (prc->data, prc->size, &skey, &iv, pt)) | ||
1073 | { | ||
1074 | GNUNET_break (0); | ||
1075 | dc->emsg = GNUNET_strdup (_ ("internal error decrypting content")); | ||
1076 | goto signal_error; | ||
1077 | } | ||
1078 | off = compute_disk_offset (GNUNET_ntohll (dc->uri->data.chk.file_length), | ||
1079 | dr->offset, | ||
1080 | dr->depth); | ||
1081 | /* save to disk */ | ||
1082 | if ((GNUNET_YES == prc->do_store) && | ||
1083 | ((NULL != dc->filename) || (is_recursive_download (dc))) && | ||
1084 | ((dr->depth == dc->treedepth) || | ||
1085 | (0 == (dc->options & GNUNET_FS_DOWNLOAD_NO_TEMPORARIES)))) | ||
1086 | { | ||
1087 | fh = GNUNET_DISK_file_open (NULL != dc->filename ? dc->filename | ||
1088 | : dc->temp_filename, | ||
1089 | GNUNET_DISK_OPEN_READWRITE | ||
1090 | | GNUNET_DISK_OPEN_CREATE, | ||
1091 | GNUNET_DISK_PERM_USER_READ | ||
1092 | | GNUNET_DISK_PERM_USER_WRITE | ||
1093 | | GNUNET_DISK_PERM_GROUP_READ | ||
1094 | | GNUNET_DISK_PERM_OTHER_READ); | ||
1095 | if (NULL == fh) | ||
1096 | { | ||
1097 | GNUNET_asprintf (&dc->emsg, | ||
1098 | _ ("Download failed: could not open file `%s': %s"), | ||
1099 | dc->filename, | ||
1100 | strerror (errno)); | ||
1101 | goto signal_error; | ||
1102 | } | ||
1103 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1104 | "Saving decrypted block to disk at offset %llu\n", | ||
1105 | (unsigned long long) off); | ||
1106 | if ((off != GNUNET_DISK_file_seek (fh, off, GNUNET_DISK_SEEK_SET))) | ||
1107 | { | ||
1108 | GNUNET_asprintf (&dc->emsg, | ||
1109 | _ ("Failed to seek to offset %llu in file `%s': %s"), | ||
1110 | (unsigned long long) off, | ||
1111 | dc->filename, | ||
1112 | strerror (errno)); | ||
1113 | goto signal_error; | ||
1114 | } | ||
1115 | if (prc->size != GNUNET_DISK_file_write (fh, pt, prc->size)) | ||
1116 | { | ||
1117 | GNUNET_asprintf ( | ||
1118 | &dc->emsg, | ||
1119 | _ ("Failed to write block of %u bytes at offset %llu in file `%s': %s"), | ||
1120 | (unsigned int) prc->size, | ||
1121 | (unsigned long long) off, | ||
1122 | dc->filename, | ||
1123 | strerror (errno)); | ||
1124 | goto signal_error; | ||
1125 | } | ||
1126 | GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fh)); | ||
1127 | fh = NULL; | ||
1128 | } | ||
1129 | |||
1130 | if (0 == dr->depth) | ||
1131 | { | ||
1132 | /* DBLOCK, update progress and try recursion if applicable */ | ||
1133 | app = prc->size; | ||
1134 | if (dr->offset < dc->offset) | ||
1135 | { | ||
1136 | /* starting offset begins in the middle of pt, | ||
1137 | * do not count first bytes as progress */ | ||
1138 | GNUNET_assert (app > (dc->offset - dr->offset)); | ||
1139 | app -= (dc->offset - dr->offset); | ||
1140 | } | ||
1141 | if (dr->offset + prc->size > dc->offset + dc->length) | ||
1142 | { | ||
1143 | /* end of block is after relevant range, | ||
1144 | * do not count last bytes as progress */ | ||
1145 | GNUNET_assert (app > | ||
1146 | (dr->offset + prc->size) - (dc->offset + dc->length)); | ||
1147 | app -= (dr->offset + prc->size) - (dc->offset + dc->length); | ||
1148 | } | ||
1149 | dc->completed += app; | ||
1150 | |||
1151 | /* do recursive download if option is set and either meta data | ||
1152 | * says it is a directory or if no meta data is given AND filename | ||
1153 | * ends in '.gnd' (top-level case) */ | ||
1154 | if (is_recursive_download (dc)) | ||
1155 | GNUNET_FS_directory_list_contents (prc->size, | ||
1156 | pt, | ||
1157 | off, | ||
1158 | &trigger_recursive_download, | ||
1159 | dc); | ||
1160 | } | ||
1161 | GNUNET_assert (dc->completed <= dc->length); | ||
1162 | dr->state = BRS_DOWNLOAD_DOWN; | ||
1163 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS; | ||
1164 | pi.value.download.specifics.progress.data = pt; | ||
1165 | pi.value.download.specifics.progress.offset = dr->offset; | ||
1166 | pi.value.download.specifics.progress.data_len = prc->size; | ||
1167 | pi.value.download.specifics.progress.depth = dr->depth; | ||
1168 | pi.value.download.specifics.progress.respect_offered = prc->respect_offered; | ||
1169 | pi.value.download.specifics.progress.num_transmissions = | ||
1170 | prc->num_transmissions; | ||
1171 | if (prc->last_transmission.abs_value_us != | ||
1172 | GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us) | ||
1173 | pi.value.download.specifics.progress.block_download_duration = | ||
1174 | GNUNET_TIME_absolute_get_duration (prc->last_transmission); | ||
1175 | else | ||
1176 | pi.value.download.specifics.progress.block_download_duration = | ||
1177 | GNUNET_TIME_UNIT_ZERO; /* found locally */ | ||
1178 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1179 | if (0 == dr->depth) | ||
1180 | propagate_up (dr); | ||
1181 | |||
1182 | if (dc->completed == dc->length) | ||
1183 | { | ||
1184 | /* download completed, signal */ | ||
1185 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1186 | "Download completed, truncating file to desired length %llu\n", | ||
1187 | (unsigned long long) GNUNET_ntohll ( | ||
1188 | dc->uri->data.chk.file_length)); | ||
1189 | /* truncate file to size (since we store IBlocks at the end) */ | ||
1190 | if (NULL != dc->filename) | ||
1191 | { | ||
1192 | if (0 != truncate (dc->filename, | ||
1193 | GNUNET_ntohll (dc->uri->data.chk.file_length))) | ||
1194 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
1195 | "truncate", | ||
1196 | dc->filename); | ||
1197 | } | ||
1198 | GNUNET_assert (0 == dr->depth); | ||
1199 | check_completed (dc); | ||
1200 | } | ||
1201 | if (0 == dr->depth) | ||
1202 | { | ||
1203 | /* bottom of the tree, no child downloads possible, just sync */ | ||
1204 | GNUNET_FS_download_sync_ (dc); | ||
1205 | return GNUNET_YES; | ||
1206 | } | ||
1207 | |||
1208 | GNUNET_log ( | ||
1209 | GNUNET_ERROR_TYPE_DEBUG, | ||
1210 | "Triggering downloads of children (this block was at depth %u and offset %llu)\n", | ||
1211 | dr->depth, | ||
1212 | (unsigned long long) dr->offset); | ||
1213 | GNUNET_assert (0 == (prc->size % sizeof(struct ContentHashKey))); | ||
1214 | chkarr = (struct ContentHashKey *) pt; | ||
1215 | for (i = dr->num_children - 1; i >= 0; i--) | ||
1216 | { | ||
1217 | drc = dr->children[i]; | ||
1218 | switch (drc->state) | ||
1219 | { | ||
1220 | case BRS_INIT: | ||
1221 | if ((drc->chk_idx + 1) * sizeof(struct ContentHashKey) > prc->size) | ||
1222 | { | ||
1223 | /* 'chkarr' does not have enough space for this chk_idx; | ||
1224 | internal error! */ | ||
1225 | GNUNET_break (0); | ||
1226 | GNUNET_assert (0); | ||
1227 | dc->emsg = GNUNET_strdup (_ ("internal error decoding tree")); | ||
1228 | goto signal_error; | ||
1229 | } | ||
1230 | drc->chk = chkarr[drc->chk_idx]; | ||
1231 | drc->state = BRS_CHK_SET; | ||
1232 | if (GNUNET_YES == dc->issue_requests) | ||
1233 | schedule_block_download (dc, drc); | ||
1234 | break; | ||
1235 | |||
1236 | case BRS_RECONSTRUCT_DOWN: | ||
1237 | GNUNET_assert (0); | ||
1238 | break; | ||
1239 | |||
1240 | case BRS_RECONSTRUCT_META_UP: | ||
1241 | GNUNET_assert (0); | ||
1242 | break; | ||
1243 | |||
1244 | case BRS_RECONSTRUCT_UP: | ||
1245 | GNUNET_assert (0); | ||
1246 | break; | ||
1247 | |||
1248 | case BRS_CHK_SET: | ||
1249 | GNUNET_assert (0); | ||
1250 | break; | ||
1251 | |||
1252 | case BRS_DOWNLOAD_DOWN: | ||
1253 | GNUNET_assert (0); | ||
1254 | break; | ||
1255 | |||
1256 | case BRS_DOWNLOAD_UP: | ||
1257 | GNUNET_assert (0); | ||
1258 | break; | ||
1259 | |||
1260 | case BRS_ERROR: | ||
1261 | GNUNET_assert (0); | ||
1262 | break; | ||
1263 | |||
1264 | default: | ||
1265 | GNUNET_assert (0); | ||
1266 | break; | ||
1267 | } | ||
1268 | } | ||
1269 | GNUNET_FS_download_sync_ (dc); | ||
1270 | return GNUNET_YES; | ||
1271 | |||
1272 | signal_error: | ||
1273 | if (NULL != fh) | ||
1274 | GNUNET_DISK_file_close (fh); | ||
1275 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR; | ||
1276 | pi.value.download.specifics.error.message = dc->emsg; | ||
1277 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1278 | GNUNET_MQ_destroy (dc->mq); | ||
1279 | dc->mq = NULL; | ||
1280 | GNUNET_FS_free_download_request_ (dc->top_request); | ||
1281 | dc->top_request = NULL; | ||
1282 | if (NULL != dc->job_queue) | ||
1283 | { | ||
1284 | GNUNET_FS_dequeue_ (dc->job_queue); | ||
1285 | dc->job_queue = NULL; | ||
1286 | } | ||
1287 | GNUNET_FS_download_sync_ (dc); | ||
1288 | return GNUNET_NO; | ||
1289 | } | ||
1290 | |||
1291 | |||
1292 | /** | ||
1293 | * Type of a function to call when we check the PUT message | ||
1294 | * from the service. | ||
1295 | * | ||
1296 | * @param cls closure | ||
1297 | * @param msg message received | ||
1298 | */ | ||
1299 | static int | ||
1300 | check_put (void *cls, const struct ClientPutMessage *cm) | ||
1301 | { | ||
1302 | /* any varsize length is OK */ | ||
1303 | return GNUNET_OK; | ||
1304 | } | ||
1305 | |||
1306 | |||
1307 | /** | ||
1308 | * Type of a function to call when we receive a message | ||
1309 | * from the service. | ||
1310 | * | ||
1311 | * @param cls closure | ||
1312 | * @param msg message received | ||
1313 | */ | ||
1314 | static void | ||
1315 | handle_put (void *cls, const struct ClientPutMessage *cm) | ||
1316 | { | ||
1317 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1318 | uint16_t msize = ntohs (cm->header.size) - sizeof(*cm); | ||
1319 | struct ProcessResultClosure prc; | ||
1320 | |||
1321 | prc.dc = dc; | ||
1322 | prc.data = &cm[1]; | ||
1323 | prc.last_transmission = GNUNET_TIME_absolute_ntoh (cm->last_transmission); | ||
1324 | prc.size = msize; | ||
1325 | prc.type = ntohl (cm->type); | ||
1326 | prc.do_store = GNUNET_YES; | ||
1327 | prc.respect_offered = ntohl (cm->respect_offered); | ||
1328 | prc.num_transmissions = ntohl (cm->num_transmissions); | ||
1329 | GNUNET_CRYPTO_hash (prc.data, msize, &prc.query); | ||
1330 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1331 | "Received result for query `%s' from FS service\n", | ||
1332 | GNUNET_h2s (&prc.query)); | ||
1333 | GNUNET_CONTAINER_multihashmap_get_multiple (dc->active, | ||
1334 | &prc.query, | ||
1335 | &process_result_with_request, | ||
1336 | &prc); | ||
1337 | } | ||
1338 | |||
1339 | |||
1340 | /** | ||
1341 | * Generic error handler, called with the appropriate error code and | ||
1342 | * the same closure specified at the creation of the message queue. | ||
1343 | * Not every message queue implementation supports an error handler. | ||
1344 | * | ||
1345 | * @param cls closure with the `struct GNUNET_FS_DownloadContext *` | ||
1346 | * @param error error code | ||
1347 | */ | ||
1348 | static void | ||
1349 | download_mq_error_handler (void *cls, enum GNUNET_MQ_Error error) | ||
1350 | { | ||
1351 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1352 | |||
1353 | if (NULL != dc->mq) | ||
1354 | { | ||
1355 | GNUNET_MQ_destroy (dc->mq); | ||
1356 | dc->mq = NULL; | ||
1357 | } | ||
1358 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1359 | "Transmitting download request failed, trying to reconnect\n"); | ||
1360 | try_reconnect (dc); | ||
1361 | } | ||
1362 | |||
1363 | |||
1364 | /** | ||
1365 | * Reconnect to the FS service and transmit our queries NOW. | ||
1366 | * | ||
1367 | * @param cls our download context | ||
1368 | */ | ||
1369 | static void | ||
1370 | do_reconnect (void *cls) | ||
1371 | { | ||
1372 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1373 | struct GNUNET_MQ_MessageHandler handlers[] = | ||
1374 | { GNUNET_MQ_hd_var_size (put, | ||
1375 | GNUNET_MESSAGE_TYPE_FS_PUT, | ||
1376 | struct ClientPutMessage, | ||
1377 | dc), | ||
1378 | GNUNET_MQ_handler_end () }; | ||
1379 | |||
1380 | dc->task = NULL; | ||
1381 | dc->mq = GNUNET_CLIENT_connect (dc->h->cfg, | ||
1382 | "fs", | ||
1383 | handlers, | ||
1384 | &download_mq_error_handler, | ||
1385 | dc); | ||
1386 | if (NULL == dc->mq) | ||
1387 | { | ||
1388 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1389 | "Connecting to `%s'-service failed, will try again.\n", | ||
1390 | "FS"); | ||
1391 | try_reconnect (dc); | ||
1392 | return; | ||
1393 | } | ||
1394 | GNUNET_CONTAINER_multihashmap_iterate (dc->active, &retry_entry, dc); | ||
1395 | } | ||
1396 | |||
1397 | |||
1398 | /** | ||
1399 | * We've lost our connection with the FS service. | ||
1400 | * Re-establish it and re-transmit all of our | ||
1401 | * pending requests. | ||
1402 | * | ||
1403 | * @param dc download context that is having trouble | ||
1404 | */ | ||
1405 | static void | ||
1406 | try_reconnect (struct GNUNET_FS_DownloadContext *dc) | ||
1407 | { | ||
1408 | if (NULL != dc->mq) | ||
1409 | { | ||
1410 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1411 | "Moving all requests back to pending list\n"); | ||
1412 | GNUNET_MQ_destroy (dc->mq); | ||
1413 | dc->mq = NULL; | ||
1414 | } | ||
1415 | if (0 == dc->reconnect_backoff.rel_value_us) | ||
1416 | dc->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS; | ||
1417 | else | ||
1418 | dc->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (dc->reconnect_backoff); | ||
1419 | |||
1420 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1421 | "Will try to reconnect in %s\n", | ||
1422 | GNUNET_STRINGS_relative_time_to_string (dc->reconnect_backoff, | ||
1423 | GNUNET_YES)); | ||
1424 | GNUNET_break (NULL != dc->job_queue); | ||
1425 | dc->task = | ||
1426 | GNUNET_SCHEDULER_add_delayed (dc->reconnect_backoff, &do_reconnect, dc); | ||
1427 | } | ||
1428 | |||
1429 | |||
1430 | /** | ||
1431 | * We're allowed to ask the FS service for our blocks. Start the download. | ||
1432 | * | ||
1433 | * @param cls the 'struct GNUNET_FS_DownloadContext' | ||
1434 | * @param mq handle to use for communication with FS (we must destroy it!) | ||
1435 | */ | ||
1436 | static void | ||
1437 | activate_fs_download (void *cls) | ||
1438 | { | ||
1439 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1440 | struct GNUNET_FS_ProgressInfo pi; | ||
1441 | |||
1442 | GNUNET_assert (NULL == dc->mq); | ||
1443 | GNUNET_assert (NULL != dc->active); | ||
1444 | do_reconnect (dc); | ||
1445 | if (NULL != dc->mq) | ||
1446 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download activated\n"); | ||
1447 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE; | ||
1448 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1449 | } | ||
1450 | |||
1451 | |||
1452 | /** | ||
1453 | * We must stop to ask the FS service for our blocks. Pause the download. | ||
1454 | * | ||
1455 | * @param cls the `struct GNUNET_FS_DownloadContext` | ||
1456 | */ | ||
1457 | static void | ||
1458 | deactivate_fs_download (void *cls) | ||
1459 | { | ||
1460 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1461 | struct GNUNET_FS_ProgressInfo pi; | ||
1462 | |||
1463 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download deactivated\n"); | ||
1464 | if (NULL != dc->mq) | ||
1465 | { | ||
1466 | GNUNET_MQ_destroy (dc->mq); | ||
1467 | dc->mq = NULL; | ||
1468 | } | ||
1469 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_INACTIVE; | ||
1470 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1471 | } | ||
1472 | |||
1473 | |||
1474 | /** | ||
1475 | * (recursively) Create a download request structure. | ||
1476 | * | ||
1477 | * @param parent parent of the current entry | ||
1478 | * @param chk_idx index of the chk for this block in the parent block | ||
1479 | * @param depth depth of the current entry, 0 are the DBLOCKs, | ||
1480 | * top level block is 'dc->treedepth - 1' | ||
1481 | * @param dr_offset offset in the original file this block maps to | ||
1482 | * (as in, offset of the first byte of the first DBLOCK | ||
1483 | * in the subtree rooted in the returned download request tree) | ||
1484 | * @param file_start_offset desired starting offset for the download | ||
1485 | * in the original file; requesting tree should not contain | ||
1486 | * DBLOCKs prior to the file_start_offset | ||
1487 | * @param desired_length desired number of bytes the user wanted to access | ||
1488 | * (from file_start_offset). Resulting tree should not contain | ||
1489 | * DBLOCKs after file_start_offset + file_length. | ||
1490 | * @return download request tree for the given range of DBLOCKs at | ||
1491 | * the specified depth | ||
1492 | */ | ||
1493 | static struct DownloadRequest * | ||
1494 | create_download_request (struct DownloadRequest *parent, | ||
1495 | unsigned int chk_idx, | ||
1496 | unsigned int depth, | ||
1497 | uint64_t dr_offset, | ||
1498 | uint64_t file_start_offset, | ||
1499 | uint64_t desired_length) | ||
1500 | { | ||
1501 | struct DownloadRequest *dr; | ||
1502 | unsigned int i; | ||
1503 | unsigned int head_skip; | ||
1504 | uint64_t child_block_size; | ||
1505 | |||
1506 | dr = GNUNET_new (struct DownloadRequest); | ||
1507 | dr->parent = parent; | ||
1508 | dr->depth = depth; | ||
1509 | dr->offset = dr_offset; | ||
1510 | dr->chk_idx = chk_idx; | ||
1511 | if (0 == depth) | ||
1512 | return dr; | ||
1513 | child_block_size = GNUNET_FS_tree_compute_tree_size (depth - 1); | ||
1514 | |||
1515 | /* calculate how many blocks at this level are not interesting | ||
1516 | * from the start (rounded down), either because of the requested | ||
1517 | * file offset or because this IBlock is further along */ | ||
1518 | if (dr_offset < file_start_offset) | ||
1519 | { | ||
1520 | head_skip = (file_start_offset - dr_offset) / child_block_size; | ||
1521 | } | ||
1522 | else | ||
1523 | { | ||
1524 | head_skip = 0; | ||
1525 | } | ||
1526 | |||
1527 | /* calculate index of last block at this level that is interesting (rounded up) */ | ||
1528 | dr->num_children = | ||
1529 | (file_start_offset + desired_length - dr_offset) / child_block_size; | ||
1530 | if (dr->num_children * child_block_size < | ||
1531 | file_start_offset + desired_length - dr_offset) | ||
1532 | dr->num_children++; /* round up */ | ||
1533 | GNUNET_assert (dr->num_children > head_skip); | ||
1534 | dr->num_children -= head_skip; | ||
1535 | if (dr->num_children > CHK_PER_INODE) | ||
1536 | dr->num_children = CHK_PER_INODE; /* cap at max */ | ||
1537 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1538 | "Block at offset %llu and depth %u has %u children\n", | ||
1539 | (unsigned long long) dr_offset, | ||
1540 | depth, | ||
1541 | dr->num_children); | ||
1542 | |||
1543 | /* now we can get the total number of *interesting* children for this block */ | ||
1544 | |||
1545 | /* why else would we have gotten here to begin with? (that'd be a bad logic error) */ | ||
1546 | GNUNET_assert (dr->num_children > 0); | ||
1547 | |||
1548 | dr->children = GNUNET_new_array (dr->num_children, struct DownloadRequest *); | ||
1549 | for (i = 0; i < dr->num_children; i++) | ||
1550 | { | ||
1551 | dr->children[i] = | ||
1552 | create_download_request (dr, | ||
1553 | i + head_skip, | ||
1554 | depth - 1, | ||
1555 | dr_offset + (i + head_skip) * child_block_size, | ||
1556 | file_start_offset, | ||
1557 | desired_length); | ||
1558 | } | ||
1559 | return dr; | ||
1560 | } | ||
1561 | |||
1562 | |||
1563 | /** | ||
1564 | * Continuation after a possible attempt to reconstruct | ||
1565 | * the current IBlock from the existing file. | ||
1566 | * | ||
1567 | * @param cls the 'struct ReconstructContext' | ||
1568 | */ | ||
1569 | static void | ||
1570 | reconstruct_cont (void *cls) | ||
1571 | { | ||
1572 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1573 | |||
1574 | /* clean up state from tree encoder */ | ||
1575 | if (NULL != dc->task) | ||
1576 | { | ||
1577 | GNUNET_SCHEDULER_cancel (dc->task); | ||
1578 | dc->task = NULL; | ||
1579 | } | ||
1580 | if (NULL != dc->rfh) | ||
1581 | { | ||
1582 | GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (dc->rfh)); | ||
1583 | dc->rfh = NULL; | ||
1584 | } | ||
1585 | /* start "normal" download */ | ||
1586 | dc->issue_requests = GNUNET_YES; | ||
1587 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting normal download\n"); | ||
1588 | schedule_block_download (dc, dc->top_request); | ||
1589 | } | ||
1590 | |||
1591 | |||
1592 | /** | ||
1593 | * Task requesting the next block from the tree encoder. | ||
1594 | * | ||
1595 | * @param cls the 'struct GNUJNET_FS_DownloadContext' we're processing | ||
1596 | */ | ||
1597 | static void | ||
1598 | get_next_block (void *cls) | ||
1599 | { | ||
1600 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1601 | |||
1602 | dc->task = NULL; | ||
1603 | GNUNET_FS_tree_encoder_next (dc->te); | ||
1604 | } | ||
1605 | |||
1606 | |||
1607 | /** | ||
1608 | * Function called asking for the current (encoded) | ||
1609 | * block to be processed. After processing the | ||
1610 | * client should either call "GNUNET_FS_tree_encode_next" | ||
1611 | * or (on error) "GNUNET_FS_tree_encode_finish". | ||
1612 | * | ||
1613 | * This function checks if the content on disk matches | ||
1614 | * the expected content based on the URI. | ||
1615 | * | ||
1616 | * @param cls closure | ||
1617 | * @param chk content hash key for the block | ||
1618 | * @param offset offset of the block | ||
1619 | * @param depth depth of the block, 0 for DBLOCK | ||
1620 | * @param type type of the block (IBLOCK or DBLOCK) | ||
1621 | * @param block the (encrypted) block | ||
1622 | * @param block_size size of block (in bytes) | ||
1623 | */ | ||
1624 | static void | ||
1625 | reconstruct_cb (void *cls, | ||
1626 | const struct ContentHashKey *chk, | ||
1627 | uint64_t offset, | ||
1628 | unsigned int depth, | ||
1629 | enum GNUNET_BLOCK_Type type, | ||
1630 | const void *block, | ||
1631 | uint16_t block_size) | ||
1632 | { | ||
1633 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1634 | struct GNUNET_FS_ProgressInfo pi; | ||
1635 | struct DownloadRequest *dr; | ||
1636 | uint64_t blen; | ||
1637 | unsigned int chld; | ||
1638 | |||
1639 | /* find corresponding request entry */ | ||
1640 | dr = dc->top_request; | ||
1641 | while (dr->depth > depth) | ||
1642 | { | ||
1643 | GNUNET_assert (dr->num_children > 0); | ||
1644 | blen = GNUNET_FS_tree_compute_tree_size (dr->depth - 1); | ||
1645 | chld = (offset - dr->offset) / blen; | ||
1646 | if (chld < dr->children[0]->chk_idx) | ||
1647 | { | ||
1648 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1649 | "Block %u < %u irrelevant for our range\n", | ||
1650 | chld, | ||
1651 | dr->children[0]->chk_idx); | ||
1652 | dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc); | ||
1653 | return; /* irrelevant block */ | ||
1654 | } | ||
1655 | if (chld > dr->children[dr->num_children - 1]->chk_idx) | ||
1656 | { | ||
1657 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1658 | "Block %u > %u irrelevant for our range\n", | ||
1659 | chld, | ||
1660 | dr->children[dr->num_children - 1]->chk_idx); | ||
1661 | dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc); | ||
1662 | return; /* irrelevant block */ | ||
1663 | } | ||
1664 | dr = dr->children[chld - dr->children[0]->chk_idx]; | ||
1665 | } | ||
1666 | GNUNET_log ( | ||
1667 | GNUNET_ERROR_TYPE_DEBUG, | ||
1668 | "Matched TE block with request at offset %llu and depth %u in state %d\n", | ||
1669 | (unsigned long long) dr->offset, | ||
1670 | dr->depth, | ||
1671 | dr->state); | ||
1672 | /* FIXME: this code needs more testing and might | ||
1673 | need to handle more states... */ | ||
1674 | switch (dr->state) | ||
1675 | { | ||
1676 | case BRS_INIT: | ||
1677 | break; | ||
1678 | |||
1679 | case BRS_RECONSTRUCT_DOWN: | ||
1680 | break; | ||
1681 | |||
1682 | case BRS_RECONSTRUCT_META_UP: | ||
1683 | break; | ||
1684 | |||
1685 | case BRS_RECONSTRUCT_UP: | ||
1686 | break; | ||
1687 | |||
1688 | case BRS_CHK_SET: | ||
1689 | if (0 == memcmp (chk, &dr->chk, sizeof(struct ContentHashKey))) | ||
1690 | { | ||
1691 | GNUNET_log ( | ||
1692 | GNUNET_ERROR_TYPE_DEBUG, | ||
1693 | "Reconstruction succeeded, can use block at offset %llu, depth %u\n", | ||
1694 | (unsigned long long) offset, | ||
1695 | depth); | ||
1696 | /* block matches, hence tree below matches; | ||
1697 | * this request is done! */ | ||
1698 | dr->state = BRS_DOWNLOAD_UP; | ||
1699 | (void) GNUNET_CONTAINER_multihashmap_remove (dc->active, | ||
1700 | &dr->chk.query, | ||
1701 | dr); | ||
1702 | /* calculate how many bytes of payload this block | ||
1703 | * corresponds to */ | ||
1704 | blen = GNUNET_FS_tree_compute_tree_size (dr->depth); | ||
1705 | /* how many of those bytes are in the requested range? */ | ||
1706 | blen = GNUNET_MIN (blen, dc->length + dc->offset - dr->offset); | ||
1707 | /* signal progress */ | ||
1708 | dc->completed += blen; | ||
1709 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS; | ||
1710 | pi.value.download.specifics.progress.data = NULL; | ||
1711 | pi.value.download.specifics.progress.offset = offset; | ||
1712 | pi.value.download.specifics.progress.data_len = 0; | ||
1713 | pi.value.download.specifics.progress.depth = 0; | ||
1714 | pi.value.download.specifics.progress.respect_offered = 0; | ||
1715 | pi.value.download.specifics.progress.block_download_duration = | ||
1716 | GNUNET_TIME_UNIT_ZERO; | ||
1717 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1718 | /* FIXME: duplicated code from 'process_result_with_request - refactor */ | ||
1719 | if (dc->completed == dc->length) | ||
1720 | { | ||
1721 | /* download completed, signal */ | ||
1722 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1723 | "Download completed, truncating file to desired length %llu\n", | ||
1724 | (unsigned long long) GNUNET_ntohll ( | ||
1725 | dc->uri->data.chk.file_length)); | ||
1726 | /* truncate file to size (since we store IBlocks at the end) */ | ||
1727 | if (NULL != dc->filename) | ||
1728 | { | ||
1729 | if (0 != truncate (dc->filename, | ||
1730 | GNUNET_ntohll (dc->uri->data.chk.file_length))) | ||
1731 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
1732 | "truncate", | ||
1733 | dc->filename); | ||
1734 | } | ||
1735 | } | ||
1736 | } | ||
1737 | else | ||
1738 | GNUNET_log ( | ||
1739 | GNUNET_ERROR_TYPE_DEBUG, | ||
1740 | "Reconstruction failed, need to download block at offset %llu, depth %u\n", | ||
1741 | (unsigned long long) offset, | ||
1742 | depth); | ||
1743 | break; | ||
1744 | |||
1745 | case BRS_DOWNLOAD_DOWN: | ||
1746 | break; | ||
1747 | |||
1748 | case BRS_DOWNLOAD_UP: | ||
1749 | break; | ||
1750 | |||
1751 | case BRS_ERROR: | ||
1752 | break; | ||
1753 | |||
1754 | default: | ||
1755 | GNUNET_assert (0); | ||
1756 | break; | ||
1757 | } | ||
1758 | dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc); | ||
1759 | if ((dr == dc->top_request) && (dr->state == BRS_DOWNLOAD_UP)) | ||
1760 | check_completed (dc); | ||
1761 | } | ||
1762 | |||
1763 | |||
1764 | /** | ||
1765 | * Function called by the tree encoder to obtain a block of plaintext | ||
1766 | * data (for the lowest level of the tree). | ||
1767 | * | ||
1768 | * @param cls our 'struct ReconstructContext' | ||
1769 | * @param offset identifies which block to get | ||
1770 | * @param max (maximum) number of bytes to get; returning | ||
1771 | * fewer will also cause errors | ||
1772 | * @param buf where to copy the plaintext buffer | ||
1773 | * @param emsg location to store an error message (on error) | ||
1774 | * @return number of bytes copied to buf, 0 on error | ||
1775 | */ | ||
1776 | static size_t | ||
1777 | fh_reader (void *cls, uint64_t offset, size_t max, void *buf, char **emsg) | ||
1778 | { | ||
1779 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1780 | struct GNUNET_DISK_FileHandle *fh = dc->rfh; | ||
1781 | ssize_t ret; | ||
1782 | |||
1783 | if (NULL != emsg) | ||
1784 | *emsg = NULL; | ||
1785 | if (offset != GNUNET_DISK_file_seek (fh, offset, GNUNET_DISK_SEEK_SET)) | ||
1786 | { | ||
1787 | if (NULL != emsg) | ||
1788 | *emsg = GNUNET_strdup (strerror (errno)); | ||
1789 | return 0; | ||
1790 | } | ||
1791 | ret = GNUNET_DISK_file_read (fh, buf, max); | ||
1792 | if (ret < 0) | ||
1793 | { | ||
1794 | if (NULL != emsg) | ||
1795 | *emsg = GNUNET_strdup (strerror (errno)); | ||
1796 | return 0; | ||
1797 | } | ||
1798 | return ret; | ||
1799 | } | ||
1800 | |||
1801 | |||
1802 | /** | ||
1803 | * Task that creates the initial (top-level) download | ||
1804 | * request for the file. | ||
1805 | * | ||
1806 | * @param cls the 'struct GNUNET_FS_DownloadContext' | ||
1807 | */ | ||
1808 | void | ||
1809 | GNUNET_FS_download_start_task_ (void *cls) | ||
1810 | { | ||
1811 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1812 | struct GNUNET_FS_ProgressInfo pi; | ||
1813 | struct GNUNET_DISK_FileHandle *fh; | ||
1814 | |||
1815 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start task running...\n"); | ||
1816 | dc->task = NULL; | ||
1817 | if (0 == dc->length) | ||
1818 | { | ||
1819 | /* no bytes required! */ | ||
1820 | if (NULL != dc->filename) | ||
1821 | { | ||
1822 | fh = GNUNET_DISK_file_open (dc->filename, | ||
1823 | GNUNET_DISK_OPEN_READWRITE | ||
1824 | | GNUNET_DISK_OPEN_CREATE | ||
1825 | | ((0 == | ||
1826 | GNUNET_FS_uri_chk_get_file_size (dc->uri)) | ||
1827 | ? GNUNET_DISK_OPEN_TRUNCATE | ||
1828 | : 0), | ||
1829 | GNUNET_DISK_PERM_USER_READ | ||
1830 | | GNUNET_DISK_PERM_USER_WRITE | ||
1831 | | GNUNET_DISK_PERM_GROUP_READ | ||
1832 | | GNUNET_DISK_PERM_OTHER_READ); | ||
1833 | GNUNET_DISK_file_close (fh); | ||
1834 | } | ||
1835 | GNUNET_FS_download_sync_ (dc); | ||
1836 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_START; | ||
1837 | pi.value.download.specifics.start.meta = dc->meta; | ||
1838 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1839 | check_completed (dc); | ||
1840 | return; | ||
1841 | } | ||
1842 | if (NULL != dc->emsg) | ||
1843 | return; | ||
1844 | if (NULL == dc->top_request) | ||
1845 | { | ||
1846 | dc->top_request = create_download_request (NULL, | ||
1847 | 0, | ||
1848 | dc->treedepth - 1, | ||
1849 | 0, | ||
1850 | dc->offset, | ||
1851 | dc->length); | ||
1852 | dc->top_request->state = BRS_CHK_SET; | ||
1853 | dc->top_request->chk = (dc->uri->type == GNUNET_FS_URI_CHK) | ||
1854 | ? dc->uri->data.chk.chk | ||
1855 | : dc->uri->data.loc.fi.chk; | ||
1856 | /* signal start */ | ||
1857 | GNUNET_FS_download_sync_ (dc); | ||
1858 | if (NULL != dc->search) | ||
1859 | GNUNET_FS_search_result_sync_ (dc->search); | ||
1860 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_START; | ||
1861 | pi.value.download.specifics.start.meta = dc->meta; | ||
1862 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1863 | } | ||
1864 | GNUNET_FS_download_start_downloading_ (dc); | ||
1865 | /* attempt reconstruction from disk */ | ||
1866 | if (GNUNET_YES == GNUNET_DISK_file_test (dc->filename)) | ||
1867 | dc->rfh = GNUNET_DISK_file_open (dc->filename, | ||
1868 | GNUNET_DISK_OPEN_READ, | ||
1869 | GNUNET_DISK_PERM_NONE); | ||
1870 | if (dc->top_request->state == BRS_CHK_SET) | ||
1871 | { | ||
1872 | if (NULL != dc->rfh) | ||
1873 | { | ||
1874 | /* first, try top-down */ | ||
1875 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1876 | "Trying top-down reconstruction for `%s'\n", | ||
1877 | dc->filename); | ||
1878 | try_top_down_reconstruction (dc, dc->top_request); | ||
1879 | switch (dc->top_request->state) | ||
1880 | { | ||
1881 | case BRS_CHK_SET: | ||
1882 | break; /* normal */ | ||
1883 | |||
1884 | case BRS_DOWNLOAD_DOWN: | ||
1885 | break; /* normal, some blocks already down */ | ||
1886 | |||
1887 | case BRS_DOWNLOAD_UP: | ||
1888 | /* already done entirely, party! */ | ||
1889 | if (NULL != dc->rfh) | ||
1890 | { | ||
1891 | /* avoid hanging on to file handle longer than | ||
1892 | * necessary */ | ||
1893 | GNUNET_DISK_file_close (dc->rfh); | ||
1894 | dc->rfh = NULL; | ||
1895 | } | ||
1896 | return; | ||
1897 | |||
1898 | case BRS_ERROR: | ||
1899 | GNUNET_asprintf (&dc->emsg, _ ("Invalid URI")); | ||
1900 | GNUNET_FS_download_sync_ (dc); | ||
1901 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR; | ||
1902 | pi.value.download.specifics.error.message = dc->emsg; | ||
1903 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1904 | return; | ||
1905 | |||
1906 | default: | ||
1907 | GNUNET_assert (0); | ||
1908 | break; | ||
1909 | } | ||
1910 | } | ||
1911 | } | ||
1912 | /* attempt reconstruction from meta data */ | ||
1913 | if ((GNUNET_FS_uri_chk_get_file_size (dc->uri) <= MAX_INLINE_SIZE) && | ||
1914 | (NULL != dc->meta)) | ||
1915 | { | ||
1916 | GNUNET_log ( | ||
1917 | GNUNET_ERROR_TYPE_DEBUG, | ||
1918 | "Trying to find embedded meta data for download of size %llu with %u bytes MD\n", | ||
1919 | (unsigned long long) GNUNET_FS_uri_chk_get_file_size (dc->uri), | ||
1920 | (unsigned int) GNUNET_CONTAINER_meta_data_get_serialized_size (dc->meta)); | ||
1921 | GNUNET_CONTAINER_meta_data_iterate (dc->meta, &match_full_data, dc); | ||
1922 | if (BRS_DOWNLOAD_UP == dc->top_request->state) | ||
1923 | { | ||
1924 | if (NULL != dc->rfh) | ||
1925 | { | ||
1926 | /* avoid hanging on to file handle longer than | ||
1927 | * necessary */ | ||
1928 | GNUNET_DISK_file_close (dc->rfh); | ||
1929 | dc->rfh = NULL; | ||
1930 | } | ||
1931 | return; /* finished, status update was already done for us */ | ||
1932 | } | ||
1933 | } | ||
1934 | if (NULL != dc->rfh) | ||
1935 | { | ||
1936 | /* finally, actually run bottom-up */ | ||
1937 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1938 | "Trying bottom-up reconstruction of file `%s'\n", | ||
1939 | dc->filename); | ||
1940 | dc->te = | ||
1941 | GNUNET_FS_tree_encoder_create (dc->h, | ||
1942 | GNUNET_FS_uri_chk_get_file_size (dc->uri), | ||
1943 | dc, | ||
1944 | &fh_reader, | ||
1945 | &reconstruct_cb, | ||
1946 | NULL, | ||
1947 | &reconstruct_cont); | ||
1948 | dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc); | ||
1949 | } | ||
1950 | else | ||
1951 | { | ||
1952 | /* simple, top-level download */ | ||
1953 | dc->issue_requests = GNUNET_YES; | ||
1954 | schedule_block_download (dc, dc->top_request); | ||
1955 | } | ||
1956 | if (BRS_DOWNLOAD_UP == dc->top_request->state) | ||
1957 | check_completed (dc); | ||
1958 | } | ||
1959 | |||
1960 | |||
1961 | /** | ||
1962 | * Create SUSPEND event for the given download operation | ||
1963 | * and then clean up our state (without stop signal). | ||
1964 | * | ||
1965 | * @param cls the 'struct GNUNET_FS_DownloadContext' to signal for | ||
1966 | */ | ||
1967 | void | ||
1968 | GNUNET_FS_download_signal_suspend_ (void *cls) | ||
1969 | { | ||
1970 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1971 | struct GNUNET_FS_ProgressInfo pi; | ||
1972 | |||
1973 | if (NULL != dc->top) | ||
1974 | GNUNET_FS_end_top (dc->h, dc->top); | ||
1975 | while (NULL != dc->child_head) | ||
1976 | GNUNET_FS_download_signal_suspend_ (dc->child_head); | ||
1977 | if (NULL != dc->search) | ||
1978 | { | ||
1979 | dc->search->download = NULL; | ||
1980 | dc->search = NULL; | ||
1981 | } | ||
1982 | if (NULL != dc->job_queue) | ||
1983 | { | ||
1984 | GNUNET_FS_dequeue_ (dc->job_queue); | ||
1985 | dc->job_queue = NULL; | ||
1986 | } | ||
1987 | if (NULL != dc->parent) | ||
1988 | GNUNET_CONTAINER_DLL_remove (dc->parent->child_head, | ||
1989 | dc->parent->child_tail, | ||
1990 | dc); | ||
1991 | if (NULL != dc->task) | ||
1992 | { | ||
1993 | GNUNET_SCHEDULER_cancel (dc->task); | ||
1994 | dc->task = NULL; | ||
1995 | } | ||
1996 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_SUSPEND; | ||
1997 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
1998 | if (NULL != dc->te) | ||
1999 | { | ||
2000 | GNUNET_FS_tree_encoder_finish (dc->te, NULL); | ||
2001 | dc->te = NULL; | ||
2002 | } | ||
2003 | if (NULL != dc->rfh) | ||
2004 | { | ||
2005 | GNUNET_DISK_file_close (dc->rfh); | ||
2006 | dc->rfh = NULL; | ||
2007 | } | ||
2008 | GNUNET_FS_free_download_request_ (dc->top_request); | ||
2009 | if (NULL != dc->active) | ||
2010 | { | ||
2011 | GNUNET_CONTAINER_multihashmap_destroy (dc->active); | ||
2012 | dc->active = NULL; | ||
2013 | } | ||
2014 | GNUNET_free (dc->filename); | ||
2015 | GNUNET_CONTAINER_meta_data_destroy (dc->meta); | ||
2016 | GNUNET_FS_uri_destroy (dc->uri); | ||
2017 | GNUNET_free (dc->temp_filename); | ||
2018 | GNUNET_free (dc->serialization); | ||
2019 | GNUNET_assert (NULL == dc->job_queue); | ||
2020 | GNUNET_free (dc); | ||
2021 | } | ||
2022 | |||
2023 | |||
2024 | /** | ||
2025 | * Helper function to setup the download context. | ||
2026 | * | ||
2027 | * @param h handle to the file sharing subsystem | ||
2028 | * @param uri the URI of the file (determines what to download); CHK or LOC URI | ||
2029 | * @param meta known metadata for the file (can be NULL) | ||
2030 | * @param filename where to store the file, maybe NULL (then no file is | ||
2031 | * created on disk and data must be grabbed from the callbacks) | ||
2032 | * @param tempname where to store temporary file data, not used if filename is non-NULL; | ||
2033 | * can be NULL (in which case we will pick a name if needed); the temporary file | ||
2034 | * may already exist, in which case we will try to use the data that is there and | ||
2035 | * if it is not what is desired, will overwrite it | ||
2036 | * @param offset at what offset should we start the download (typically 0) | ||
2037 | * @param length how many bytes should be downloaded starting at offset | ||
2038 | * @param anonymity anonymity level to use for the download | ||
2039 | * @param options various options | ||
2040 | * @param cctx initial value for the client context for this download | ||
2041 | * @return context that can be used to control this download | ||
2042 | */ | ||
2043 | struct GNUNET_FS_DownloadContext * | ||
2044 | create_download_context (struct GNUNET_FS_Handle *h, | ||
2045 | const struct GNUNET_FS_Uri *uri, | ||
2046 | const struct GNUNET_CONTAINER_MetaData *meta, | ||
2047 | const char *filename, | ||
2048 | const char *tempname, | ||
2049 | uint64_t offset, | ||
2050 | uint64_t length, | ||
2051 | uint32_t anonymity, | ||
2052 | enum GNUNET_FS_DownloadOptions options, | ||
2053 | void *cctx) | ||
2054 | { | ||
2055 | struct GNUNET_FS_DownloadContext *dc; | ||
2056 | |||
2057 | GNUNET_assert (GNUNET_FS_uri_test_chk (uri) || GNUNET_FS_uri_test_loc (uri)); | ||
2058 | if ((offset + length < offset) || | ||
2059 | (offset + length > GNUNET_FS_uri_chk_get_file_size (uri))) | ||
2060 | { | ||
2061 | GNUNET_break (0); | ||
2062 | return NULL; | ||
2063 | } | ||
2064 | dc = GNUNET_new (struct GNUNET_FS_DownloadContext); | ||
2065 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2066 | "Starting download %p, %u bytes at offset %llu\n", | ||
2067 | dc, | ||
2068 | (unsigned int) length, | ||
2069 | (unsigned long long) offset); | ||
2070 | dc->h = h; | ||
2071 | dc->uri = GNUNET_FS_uri_dup (uri); | ||
2072 | dc->meta = GNUNET_CONTAINER_meta_data_duplicate (meta); | ||
2073 | dc->client_info = cctx; | ||
2074 | dc->start_time = GNUNET_TIME_absolute_get (); | ||
2075 | if (NULL != filename) | ||
2076 | { | ||
2077 | dc->filename = GNUNET_strdup (filename); | ||
2078 | if (GNUNET_YES == GNUNET_DISK_file_test (filename)) | ||
2079 | GNUNET_break (GNUNET_OK == GNUNET_DISK_file_size (filename, | ||
2080 | &dc->old_file_size, | ||
2081 | GNUNET_YES, | ||
2082 | GNUNET_YES)); | ||
2083 | } | ||
2084 | if (GNUNET_FS_uri_test_loc (dc->uri)) | ||
2085 | GNUNET_assert (GNUNET_OK == | ||
2086 | GNUNET_FS_uri_loc_get_peer_identity (dc->uri, &dc->target)); | ||
2087 | dc->offset = offset; | ||
2088 | dc->length = length; | ||
2089 | dc->anonymity = anonymity; | ||
2090 | dc->options = options; | ||
2091 | dc->active = | ||
2092 | GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE), | ||
2093 | GNUNET_NO); | ||
2094 | dc->treedepth = | ||
2095 | GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size (dc->uri)); | ||
2096 | if ((NULL == filename) && (is_recursive_download (dc))) | ||
2097 | { | ||
2098 | if (NULL != tempname) | ||
2099 | dc->temp_filename = GNUNET_strdup (tempname); | ||
2100 | else | ||
2101 | dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp"); | ||
2102 | } | ||
2103 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2104 | "Starting download `%s' of %llu bytes with tree depth %u\n", | ||
2105 | filename, | ||
2106 | (unsigned long long) length, | ||
2107 | dc->treedepth); | ||
2108 | GNUNET_assert (NULL == dc->job_queue); | ||
2109 | dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc); | ||
2110 | return dc; | ||
2111 | } | ||
2112 | |||
2113 | |||
2114 | /** | ||
2115 | * Download parts of a file. Note that this will store | ||
2116 | * the blocks at the respective offset in the given file. Also, the | ||
2117 | * download is still using the blocking of the underlying FS | ||
2118 | * encoding. As a result, the download may *write* outside of the | ||
2119 | * given boundaries (if offset and length do not match the 32k FS | ||
2120 | * block boundaries). <p> | ||
2121 | * | ||
2122 | * This function should be used to focus a download towards a | ||
2123 | * particular portion of the file (optimization), not to strictly | ||
2124 | * limit the download to exactly those bytes. | ||
2125 | * | ||
2126 | * @param h handle to the file sharing subsystem | ||
2127 | * @param uri the URI of the file (determines what to download); CHK or LOC URI | ||
2128 | * @param meta known metadata for the file (can be NULL) | ||
2129 | * @param filename where to store the file, maybe NULL (then no file is | ||
2130 | * created on disk and data must be grabbed from the callbacks) | ||
2131 | * @param tempname where to store temporary file data, not used if filename is non-NULL; | ||
2132 | * can be NULL (in which case we will pick a name if needed); the temporary file | ||
2133 | * may already exist, in which case we will try to use the data that is there and | ||
2134 | * if it is not what is desired, will overwrite it | ||
2135 | * @param offset at what offset should we start the download (typically 0) | ||
2136 | * @param length how many bytes should be downloaded starting at offset | ||
2137 | * @param anonymity anonymity level to use for the download | ||
2138 | * @param options various options | ||
2139 | * @param cctx initial value for the client context for this download | ||
2140 | * @param parent parent download to associate this download with (use NULL | ||
2141 | * for top-level downloads; useful for manually-triggered recursive downloads) | ||
2142 | * @return context that can be used to control this download | ||
2143 | */ | ||
2144 | struct GNUNET_FS_DownloadContext * | ||
2145 | GNUNET_FS_download_start (struct GNUNET_FS_Handle *h, | ||
2146 | const struct GNUNET_FS_Uri *uri, | ||
2147 | const struct GNUNET_CONTAINER_MetaData *meta, | ||
2148 | const char *filename, | ||
2149 | const char *tempname, | ||
2150 | uint64_t offset, | ||
2151 | uint64_t length, | ||
2152 | uint32_t anonymity, | ||
2153 | enum GNUNET_FS_DownloadOptions options, | ||
2154 | void *cctx, | ||
2155 | struct GNUNET_FS_DownloadContext *parent) | ||
2156 | { | ||
2157 | struct GNUNET_FS_DownloadContext *dc; | ||
2158 | |||
2159 | dc = create_download_context (h, | ||
2160 | uri, | ||
2161 | meta, | ||
2162 | filename, | ||
2163 | tempname, | ||
2164 | offset, | ||
2165 | length, | ||
2166 | anonymity, | ||
2167 | options, | ||
2168 | cctx); | ||
2169 | if (NULL == dc) | ||
2170 | return NULL; | ||
2171 | dc->parent = parent; | ||
2172 | if (NULL != parent) | ||
2173 | GNUNET_CONTAINER_DLL_insert (parent->child_head, parent->child_tail, dc); | ||
2174 | else if (0 == (GNUNET_FS_DOWNLOAD_IS_PROBE & options)) | ||
2175 | dc->top = | ||
2176 | GNUNET_FS_make_top (dc->h, &GNUNET_FS_download_signal_suspend_, dc); | ||
2177 | return dc; | ||
2178 | } | ||
2179 | |||
2180 | |||
2181 | /** | ||
2182 | * Download parts of a file based on a search result. The download | ||
2183 | * will be associated with the search result (and the association | ||
2184 | * will be preserved when serializing/deserializing the state). | ||
2185 | * If the search is stopped, the download will not be aborted but | ||
2186 | * be 'promoted' to a stand-alone download. | ||
2187 | * | ||
2188 | * As with the other download function, this will store | ||
2189 | * the blocks at the respective offset in the given file. Also, the | ||
2190 | * download is still using the blocking of the underlying FS | ||
2191 | * encoding. As a result, the download may *write* outside of the | ||
2192 | * given boundaries (if offset and length do not match the 32k FS | ||
2193 | * block boundaries). <p> | ||
2194 | * | ||
2195 | * The given range can be used to focus a download towards a | ||
2196 | * particular portion of the file (optimization), not to strictly | ||
2197 | * limit the download to exactly those bytes. | ||
2198 | * | ||
2199 | * @param h handle to the file sharing subsystem | ||
2200 | * @param sr the search result to use for the download (determines uri and | ||
2201 | * meta data and associations) | ||
2202 | * @param filename where to store the file, maybe NULL (then no file is | ||
2203 | * created on disk and data must be grabbed from the callbacks) | ||
2204 | * @param tempname where to store temporary file data, not used if filename is non-NULL; | ||
2205 | * can be NULL (in which case we will pick a name if needed); the temporary file | ||
2206 | * may already exist, in which case we will try to use the data that is there and | ||
2207 | * if it is not what is desired, will overwrite it | ||
2208 | * @param offset at what offset should we start the download (typically 0) | ||
2209 | * @param length how many bytes should be downloaded starting at offset | ||
2210 | * @param anonymity anonymity level to use for the download | ||
2211 | * @param options various download options | ||
2212 | * @param cctx initial value for the client context for this download | ||
2213 | * @return context that can be used to control this download | ||
2214 | */ | ||
2215 | struct GNUNET_FS_DownloadContext * | ||
2216 | GNUNET_FS_download_start_from_search (struct GNUNET_FS_Handle *h, | ||
2217 | struct GNUNET_FS_SearchResult *sr, | ||
2218 | const char *filename, | ||
2219 | const char *tempname, | ||
2220 | uint64_t offset, | ||
2221 | uint64_t length, | ||
2222 | uint32_t anonymity, | ||
2223 | enum GNUNET_FS_DownloadOptions options, | ||
2224 | void *cctx) | ||
2225 | { | ||
2226 | struct GNUNET_FS_DownloadContext *dc; | ||
2227 | |||
2228 | if ((NULL == sr) || (NULL != sr->download)) | ||
2229 | { | ||
2230 | GNUNET_break (0); | ||
2231 | return NULL; | ||
2232 | } | ||
2233 | dc = create_download_context (h, | ||
2234 | sr->uri, | ||
2235 | sr->meta, | ||
2236 | filename, | ||
2237 | tempname, | ||
2238 | offset, | ||
2239 | length, | ||
2240 | anonymity, | ||
2241 | options, | ||
2242 | cctx); | ||
2243 | if (NULL == dc) | ||
2244 | return NULL; | ||
2245 | dc->search = sr; | ||
2246 | sr->download = dc; | ||
2247 | if (NULL != sr->probe_ctx) | ||
2248 | { | ||
2249 | GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES); | ||
2250 | sr->probe_ctx = NULL; | ||
2251 | GNUNET_FS_stop_probe_ping_task_ (sr); | ||
2252 | } | ||
2253 | return dc; | ||
2254 | } | ||
2255 | |||
2256 | |||
2257 | /** | ||
2258 | * Start the downloading process (by entering the queue). | ||
2259 | * | ||
2260 | * @param dc our download context | ||
2261 | */ | ||
2262 | void | ||
2263 | GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc) | ||
2264 | { | ||
2265 | if (dc->completed == dc->length) | ||
2266 | return; | ||
2267 | if (NULL != dc->mq) | ||
2268 | return; /* already running */ | ||
2269 | GNUNET_assert (NULL == dc->job_queue); | ||
2270 | GNUNET_assert (NULL == dc->task); | ||
2271 | GNUNET_assert (NULL != dc->active); | ||
2272 | dc->job_queue = | ||
2273 | GNUNET_FS_queue_ (dc->h, | ||
2274 | &activate_fs_download, | ||
2275 | &deactivate_fs_download, | ||
2276 | dc, | ||
2277 | (dc->length + DBLOCK_SIZE - 1) / DBLOCK_SIZE, | ||
2278 | (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE)) | ||
2279 | ? GNUNET_FS_QUEUE_PRIORITY_NORMAL | ||
2280 | : GNUNET_FS_QUEUE_PRIORITY_PROBE); | ||
2281 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2282 | "Download %p put into queue as job %p\n", | ||
2283 | dc, | ||
2284 | dc->job_queue); | ||
2285 | } | ||
2286 | |||
2287 | |||
2288 | /** | ||
2289 | * Suspend a download. | ||
2290 | * | ||
2291 | * @param dc handle for the download | ||
2292 | */ | ||
2293 | void | ||
2294 | GNUNET_FS_download_suspend (struct GNUNET_FS_DownloadContext *dc) | ||
2295 | { | ||
2296 | deactivate_fs_download (dc); | ||
2297 | } | ||
2298 | |||
2299 | |||
2300 | /** | ||
2301 | * Resume a suspended download. | ||
2302 | * | ||
2303 | * @param dc handle for the download | ||
2304 | */ | ||
2305 | void | ||
2306 | GNUNET_FS_download_resume (struct GNUNET_FS_DownloadContext *dc) | ||
2307 | { | ||
2308 | struct GNUNET_FS_ProgressInfo pi; | ||
2309 | |||
2310 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE; | ||
2311 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
2312 | |||
2313 | GNUNET_assert (NULL == dc->task); | ||
2314 | dc->job_queue = | ||
2315 | GNUNET_FS_queue_ (dc->h, | ||
2316 | &activate_fs_download, | ||
2317 | &deactivate_fs_download, | ||
2318 | dc, | ||
2319 | (dc->length + DBLOCK_SIZE - 1) / DBLOCK_SIZE, | ||
2320 | (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE)) | ||
2321 | ? GNUNET_FS_QUEUE_PRIORITY_NORMAL | ||
2322 | : GNUNET_FS_QUEUE_PRIORITY_PROBE); | ||
2323 | } | ||
2324 | |||
2325 | |||
2326 | /** | ||
2327 | * Stop a download (aborts if download is incomplete). | ||
2328 | * | ||
2329 | * @param dc handle for the download | ||
2330 | * @param do_delete delete files of incomplete downloads | ||
2331 | */ | ||
2332 | void | ||
2333 | GNUNET_FS_download_stop (struct GNUNET_FS_DownloadContext *dc, int do_delete) | ||
2334 | { | ||
2335 | struct GNUNET_FS_ProgressInfo pi; | ||
2336 | int have_children; | ||
2337 | int search_was_null; | ||
2338 | |||
2339 | if (NULL != dc->top) | ||
2340 | GNUNET_FS_end_top (dc->h, dc->top); | ||
2341 | if (NULL != dc->task) | ||
2342 | { | ||
2343 | GNUNET_SCHEDULER_cancel (dc->task); | ||
2344 | dc->task = NULL; | ||
2345 | } | ||
2346 | search_was_null = (NULL == dc->search); | ||
2347 | if (NULL != dc->search) | ||
2348 | { | ||
2349 | dc->search->download = NULL; | ||
2350 | GNUNET_FS_search_result_sync_ (dc->search); | ||
2351 | dc->search = NULL; | ||
2352 | } | ||
2353 | if (NULL != dc->job_queue) | ||
2354 | { | ||
2355 | GNUNET_FS_dequeue_ (dc->job_queue); | ||
2356 | dc->job_queue = NULL; | ||
2357 | } | ||
2358 | if (NULL != dc->te) | ||
2359 | { | ||
2360 | GNUNET_FS_tree_encoder_finish (dc->te, NULL); | ||
2361 | dc->te = NULL; | ||
2362 | } | ||
2363 | have_children = (NULL != dc->child_head) ? GNUNET_YES : GNUNET_NO; | ||
2364 | while (NULL != dc->child_head) | ||
2365 | GNUNET_FS_download_stop (dc->child_head, do_delete); | ||
2366 | if (NULL != dc->parent) | ||
2367 | GNUNET_CONTAINER_DLL_remove (dc->parent->child_head, | ||
2368 | dc->parent->child_tail, | ||
2369 | dc); | ||
2370 | if (NULL != dc->serialization) | ||
2371 | GNUNET_FS_remove_sync_file_ (dc->h, | ||
2372 | ((NULL != dc->parent) || (! search_was_null)) | ||
2373 | ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD | ||
2374 | : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD, | ||
2375 | dc->serialization); | ||
2376 | if ((GNUNET_YES == have_children) && (NULL == dc->parent)) | ||
2377 | GNUNET_FS_remove_sync_dir_ (dc->h, | ||
2378 | (! search_was_null) | ||
2379 | ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD | ||
2380 | : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD, | ||
2381 | dc->serialization); | ||
2382 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_STOPPED; | ||
2383 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
2384 | GNUNET_FS_free_download_request_ (dc->top_request); | ||
2385 | dc->top_request = NULL; | ||
2386 | if (NULL != dc->active) | ||
2387 | { | ||
2388 | GNUNET_CONTAINER_multihashmap_destroy (dc->active); | ||
2389 | dc->active = NULL; | ||
2390 | } | ||
2391 | if (NULL != dc->filename) | ||
2392 | { | ||
2393 | if ((dc->completed != dc->length) && (GNUNET_YES == do_delete)) | ||
2394 | { | ||
2395 | if ((0 != unlink (dc->filename)) && (ENOENT != errno)) | ||
2396 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
2397 | "unlink", | ||
2398 | dc->filename); | ||
2399 | } | ||
2400 | GNUNET_free (dc->filename); | ||
2401 | } | ||
2402 | GNUNET_CONTAINER_meta_data_destroy (dc->meta); | ||
2403 | GNUNET_FS_uri_destroy (dc->uri); | ||
2404 | if (NULL != dc->temp_filename) | ||
2405 | { | ||
2406 | if (0 != unlink (dc->temp_filename)) | ||
2407 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, | ||
2408 | "unlink", | ||
2409 | dc->temp_filename); | ||
2410 | GNUNET_free (dc->temp_filename); | ||
2411 | } | ||
2412 | GNUNET_free (dc->serialization); | ||
2413 | GNUNET_assert (NULL == dc->job_queue); | ||
2414 | GNUNET_free (dc); | ||
2415 | } | ||
2416 | |||
2417 | |||
2418 | /* end of fs_download.c */ | ||