diff options
Diffstat (limited to 'src/fs/fs_api.c')
-rw-r--r-- | src/fs/fs_api.c | 3349 |
1 files changed, 0 insertions, 3349 deletions
diff --git a/src/fs/fs_api.c b/src/fs/fs_api.c deleted file mode 100644 index 63b17a93e..000000000 --- a/src/fs/fs_api.c +++ /dev/null | |||
@@ -1,3349 +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 | /** | ||
22 | * @file fs/fs_api.c | ||
23 | * @brief main FS functions (master initialization, serialization, deserialization, shared code) | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | |||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_fs_service.h" | ||
30 | #include "fs_api.h" | ||
31 | #include "fs_tree.h" | ||
32 | |||
33 | /** | ||
34 | * How many block requests can we have outstanding in parallel at a time by default? | ||
35 | */ | ||
36 | #define DEFAULT_MAX_PARALLEL_REQUESTS (1024 * 10) | ||
37 | |||
38 | /** | ||
39 | * How many downloads can we have outstanding in parallel at a time by default? | ||
40 | */ | ||
41 | #define DEFAULT_MAX_PARALLEL_DOWNLOADS 16 | ||
42 | |||
43 | /** | ||
44 | * Start the given job (send signal, remove from pending queue, update | ||
45 | * counters and state). | ||
46 | * | ||
47 | * @param qe job to start | ||
48 | */ | ||
49 | static void | ||
50 | start_job (struct GNUNET_FS_QueueEntry *qe) | ||
51 | { | ||
52 | qe->active = GNUNET_YES; | ||
53 | qe->start (qe->cls); | ||
54 | qe->start_times++; | ||
55 | qe->h->active_blocks += qe->blocks; | ||
56 | qe->h->active_downloads++; | ||
57 | qe->start_time = GNUNET_TIME_absolute_get (); | ||
58 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
59 | "Starting job %p (%u active)\n", | ||
60 | qe, | ||
61 | qe->h->active_downloads); | ||
62 | GNUNET_CONTAINER_DLL_remove (qe->h->pending_head, qe->h->pending_tail, qe); | ||
63 | GNUNET_CONTAINER_DLL_insert_after (qe->h->running_head, | ||
64 | qe->h->running_tail, | ||
65 | qe->h->running_tail, | ||
66 | qe); | ||
67 | } | ||
68 | |||
69 | |||
70 | /** | ||
71 | * Stop the given job (send signal, remove from active queue, update | ||
72 | * counters and state). | ||
73 | * | ||
74 | * @param qe job to stop | ||
75 | */ | ||
76 | static void | ||
77 | stop_job (struct GNUNET_FS_QueueEntry *qe) | ||
78 | { | ||
79 | qe->active = GNUNET_NO; | ||
80 | qe->stop (qe->cls); | ||
81 | GNUNET_assert (0 < qe->h->active_downloads); | ||
82 | qe->h->active_downloads--; | ||
83 | qe->h->active_blocks -= qe->blocks; | ||
84 | qe->run_time = GNUNET_TIME_relative_add (qe->run_time, | ||
85 | GNUNET_TIME_absolute_get_duration ( | ||
86 | qe->start_time)); | ||
87 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
88 | "Stopping job %p (%u active)\n", | ||
89 | qe, | ||
90 | qe->h->active_downloads); | ||
91 | GNUNET_CONTAINER_DLL_remove (qe->h->running_head, qe->h->running_tail, qe); | ||
92 | GNUNET_CONTAINER_DLL_insert_after (qe->h->pending_head, | ||
93 | qe->h->pending_tail, | ||
94 | qe->h->pending_tail, | ||
95 | qe); | ||
96 | } | ||
97 | |||
98 | |||
99 | /** | ||
100 | * Process the jobs in the job queue, possibly starting some | ||
101 | * and stopping others. | ||
102 | * | ||
103 | * @param cls the `struct GNUNET_FS_Handle *` | ||
104 | */ | ||
105 | static void | ||
106 | process_job_queue (void *cls) | ||
107 | { | ||
108 | struct GNUNET_FS_Handle *h = cls; | ||
109 | struct GNUNET_FS_QueueEntry *qe; | ||
110 | struct GNUNET_FS_QueueEntry *next; | ||
111 | struct GNUNET_TIME_Relative run_time; | ||
112 | struct GNUNET_TIME_Relative restart_at; | ||
113 | struct GNUNET_TIME_Relative rst; | ||
114 | struct GNUNET_TIME_Absolute end_time; | ||
115 | unsigned int num_downloads_waiting; | ||
116 | unsigned int num_downloads_active; | ||
117 | unsigned int num_downloads_expired; | ||
118 | unsigned int num_probes_active; | ||
119 | unsigned int num_probes_waiting; | ||
120 | unsigned int num_probes_expired; | ||
121 | int num_probes_change; | ||
122 | int num_downloads_change; | ||
123 | int block_limit_hit; | ||
124 | |||
125 | h->queue_job = NULL; | ||
126 | /* restart_at will be set to the time when it makes sense to | ||
127 | re-evaluate the job queue (unless, of course, jobs complete | ||
128 | or are added, then we'll be triggered immediately */ | ||
129 | restart_at = GNUNET_TIME_UNIT_FOREVER_REL; | ||
130 | /* first, calculate some basic statistics on pending jobs */ | ||
131 | num_probes_waiting = 0; | ||
132 | num_downloads_waiting = 0; | ||
133 | for (qe = h->pending_head; NULL != qe; qe = qe->next) | ||
134 | { | ||
135 | switch (qe->priority) | ||
136 | { | ||
137 | case GNUNET_FS_QUEUE_PRIORITY_PROBE: | ||
138 | num_probes_waiting++; | ||
139 | break; | ||
140 | |||
141 | case GNUNET_FS_QUEUE_PRIORITY_NORMAL: | ||
142 | num_downloads_waiting++; | ||
143 | break; | ||
144 | |||
145 | default: | ||
146 | GNUNET_break (0); | ||
147 | break; | ||
148 | } | ||
149 | } | ||
150 | /* now, calculate some basic statistics on running jobs */ | ||
151 | num_probes_active = 0; | ||
152 | num_probes_expired = 0; | ||
153 | num_downloads_active = 0; | ||
154 | num_downloads_expired = 0; | ||
155 | next = h->running_head; | ||
156 | while (NULL != (qe = next)) | ||
157 | { | ||
158 | next = qe->next; | ||
159 | switch (qe->priority) | ||
160 | { | ||
161 | case GNUNET_FS_QUEUE_PRIORITY_PROBE: | ||
162 | run_time = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2); | ||
163 | end_time = GNUNET_TIME_absolute_add (qe->start_time, run_time); | ||
164 | rst = GNUNET_TIME_absolute_get_remaining (end_time); | ||
165 | if (0 == rst.rel_value_us) | ||
166 | { | ||
167 | num_probes_expired++; | ||
168 | stop_job (qe); | ||
169 | } | ||
170 | else | ||
171 | { | ||
172 | num_probes_active++; | ||
173 | restart_at = GNUNET_TIME_relative_min (rst, restart_at); | ||
174 | } | ||
175 | break; | ||
176 | |||
177 | case GNUNET_FS_QUEUE_PRIORITY_NORMAL: | ||
178 | run_time = | ||
179 | GNUNET_TIME_relative_saturating_multiply (h->avg_block_latency, | ||
180 | qe->blocks * qe->start_times); | ||
181 | end_time = GNUNET_TIME_absolute_add (qe->start_time, run_time); | ||
182 | rst = GNUNET_TIME_absolute_get_remaining (end_time); | ||
183 | if (0 == rst.rel_value_us) | ||
184 | { | ||
185 | num_downloads_expired++; | ||
186 | stop_job (qe); | ||
187 | } | ||
188 | else | ||
189 | { | ||
190 | num_downloads_active++; | ||
191 | restart_at = GNUNET_TIME_relative_min (rst, restart_at); | ||
192 | } | ||
193 | break; | ||
194 | |||
195 | default: | ||
196 | GNUNET_break (0); | ||
197 | break; | ||
198 | } | ||
199 | } | ||
200 | GNUNET_break (h->active_downloads == | ||
201 | num_downloads_active + num_probes_active); | ||
202 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
203 | "PA: %u, PE: %u, PW: %u; DA: %u, DE: %u, DW: %u\n", | ||
204 | num_probes_active, | ||
205 | num_probes_expired, | ||
206 | num_probes_waiting, | ||
207 | num_downloads_active, | ||
208 | num_downloads_expired, | ||
209 | num_downloads_waiting); | ||
210 | GNUNET_break (h->active_downloads + num_probes_active <= | ||
211 | h->max_parallel_downloads); | ||
212 | /* calculate start/stop decisions */ | ||
213 | if (h->active_downloads + num_downloads_waiting > h->max_parallel_downloads) | ||
214 | { | ||
215 | /* stop as many probes as there are downloads and probes */ | ||
216 | num_probes_change = -GNUNET_MIN (num_probes_active, num_downloads_waiting); | ||
217 | /* start as many downloads as there are free slots, including those | ||
218 | we just opened up */ | ||
219 | num_downloads_change = | ||
220 | h->max_parallel_downloads - h->active_downloads - num_probes_change; | ||
221 | } | ||
222 | else | ||
223 | { | ||
224 | /* start all downloads (we can) */ | ||
225 | num_downloads_change = num_downloads_waiting; | ||
226 | /* also start probes if there is room, but use a lower cap of (mpd/4) + 1 */ | ||
227 | if (1 + h->max_parallel_downloads / 4 >= | ||
228 | (h->active_downloads + num_downloads_change)) | ||
229 | num_probes_change = | ||
230 | GNUNET_MIN (num_probes_waiting, | ||
231 | (1 + h->max_parallel_downloads / 4) | ||
232 | - (h->active_downloads + num_downloads_change)); | ||
233 | else | ||
234 | num_probes_change = 0; | ||
235 | } | ||
236 | GNUNET_break (num_downloads_change <= num_downloads_waiting); | ||
237 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
238 | "Changing %d probes and %d/%u/%u downloads\n", | ||
239 | num_probes_change, | ||
240 | num_downloads_change, | ||
241 | (unsigned int) h->active_downloads, | ||
242 | (unsigned int) h->max_parallel_downloads); | ||
243 | /* actually stop probes */ | ||
244 | next = h->running_head; | ||
245 | while (NULL != (qe = next)) | ||
246 | { | ||
247 | next = qe->next; | ||
248 | if (GNUNET_FS_QUEUE_PRIORITY_PROBE != qe->priority) | ||
249 | continue; | ||
250 | if (num_probes_change < 0) | ||
251 | { | ||
252 | stop_job (qe); | ||
253 | num_probes_change++; | ||
254 | if (0 == num_probes_change) | ||
255 | break; | ||
256 | } | ||
257 | } | ||
258 | GNUNET_break (0 <= num_probes_change); | ||
259 | |||
260 | /* start some more tasks if we now have empty slots */ | ||
261 | block_limit_hit = GNUNET_NO; | ||
262 | next = h->pending_head; | ||
263 | while ((NULL != (qe = next)) && | ||
264 | ((num_probes_change > 0) || (num_downloads_change > 0))) | ||
265 | { | ||
266 | next = qe->next; | ||
267 | switch (qe->priority) | ||
268 | { | ||
269 | case GNUNET_FS_QUEUE_PRIORITY_PROBE: | ||
270 | if (num_probes_change > 0) | ||
271 | { | ||
272 | start_job (qe); | ||
273 | num_probes_change--; | ||
274 | run_time = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2); | ||
275 | restart_at = GNUNET_TIME_relative_min (run_time, restart_at); | ||
276 | } | ||
277 | break; | ||
278 | |||
279 | case GNUNET_FS_QUEUE_PRIORITY_NORMAL: | ||
280 | if ((num_downloads_change > 0) && | ||
281 | ((qe->blocks + h->active_blocks <= h->max_parallel_requests) || | ||
282 | ((qe->blocks > h->max_parallel_requests) && | ||
283 | (0 == h->active_downloads)))) | ||
284 | { | ||
285 | start_job (qe); | ||
286 | num_downloads_change--; | ||
287 | } | ||
288 | else if (num_downloads_change > 0) | ||
289 | block_limit_hit = GNUNET_YES; | ||
290 | break; | ||
291 | |||
292 | default: | ||
293 | GNUNET_break (0); | ||
294 | break; | ||
295 | } | ||
296 | } | ||
297 | GNUNET_break ((0 == num_downloads_change) || (GNUNET_YES == block_limit_hit)); | ||
298 | GNUNET_break (0 == num_probes_change); | ||
299 | |||
300 | GNUNET_log ( | ||
301 | GNUNET_ERROR_TYPE_DEBUG, | ||
302 | "AD: %u, MP: %u; %d probes and %d downloads to start, will run again in %s\n", | ||
303 | h->active_downloads, | ||
304 | h->max_parallel_requests, | ||
305 | num_probes_change, | ||
306 | num_downloads_change, | ||
307 | GNUNET_STRINGS_relative_time_to_string (restart_at, GNUNET_YES)); | ||
308 | |||
309 | /* make sure we run again, callbacks might have | ||
310 | already re-scheduled the job, so cancel such | ||
311 | an operation (if it exists) */ | ||
312 | if (NULL != h->queue_job) | ||
313 | GNUNET_SCHEDULER_cancel (h->queue_job); | ||
314 | h->queue_job = | ||
315 | GNUNET_SCHEDULER_add_delayed (restart_at, &process_job_queue, h); | ||
316 | } | ||
317 | |||
318 | |||
319 | /** | ||
320 | * Add a job to the queue. | ||
321 | * | ||
322 | * @param h handle to the overall FS state | ||
323 | * @param start function to call to begin the job | ||
324 | * @param stop function to call to pause the job, or on dequeue (if the job was running) | ||
325 | * @param cls closure for start and stop | ||
326 | * @param blocks number of blocks this jobs uses | ||
327 | * @param priority how important is this download | ||
328 | * @return queue handle | ||
329 | */ | ||
330 | struct GNUNET_FS_QueueEntry * | ||
331 | GNUNET_FS_queue_ (struct GNUNET_FS_Handle *h, | ||
332 | GNUNET_SCHEDULER_TaskCallback start, | ||
333 | GNUNET_SCHEDULER_TaskCallback stop, | ||
334 | void *cls, | ||
335 | unsigned int blocks, | ||
336 | enum GNUNET_FS_QueuePriority priority) | ||
337 | { | ||
338 | struct GNUNET_FS_QueueEntry *qe; | ||
339 | |||
340 | qe = GNUNET_new (struct GNUNET_FS_QueueEntry); | ||
341 | qe->h = h; | ||
342 | qe->start = start; | ||
343 | qe->stop = stop; | ||
344 | qe->cls = cls; | ||
345 | qe->queue_time = GNUNET_TIME_absolute_get (); | ||
346 | qe->blocks = blocks; | ||
347 | qe->priority = priority; | ||
348 | GNUNET_CONTAINER_DLL_insert_after (h->pending_head, | ||
349 | h->pending_tail, | ||
350 | h->pending_tail, | ||
351 | qe); | ||
352 | if (NULL != h->queue_job) | ||
353 | GNUNET_SCHEDULER_cancel (h->queue_job); | ||
354 | h->queue_job = GNUNET_SCHEDULER_add_now (&process_job_queue, h); | ||
355 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Queueing job %p\n", qe); | ||
356 | return qe; | ||
357 | } | ||
358 | |||
359 | |||
360 | /** | ||
361 | * Dequeue a job from the queue. | ||
362 | * | ||
363 | * @param qe handle for the job | ||
364 | */ | ||
365 | void | ||
366 | GNUNET_FS_dequeue_ (struct GNUNET_FS_QueueEntry *qe) | ||
367 | { | ||
368 | struct GNUNET_FS_Handle *h; | ||
369 | |||
370 | h = qe->h; | ||
371 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Dequeueing job %p\n", qe); | ||
372 | if (GNUNET_YES == qe->active) | ||
373 | stop_job (qe); | ||
374 | GNUNET_CONTAINER_DLL_remove (h->pending_head, h->pending_tail, qe); | ||
375 | GNUNET_free (qe); | ||
376 | if (NULL != h->queue_job) | ||
377 | GNUNET_SCHEDULER_cancel (h->queue_job); | ||
378 | h->queue_job = GNUNET_SCHEDULER_add_now (&process_job_queue, h); | ||
379 | } | ||
380 | |||
381 | |||
382 | /** | ||
383 | * Create a top-level activity entry. | ||
384 | * | ||
385 | * @param h global fs handle | ||
386 | * @param ssf suspend signal function to use | ||
387 | * @param ssf_cls closure for @a ssf | ||
388 | * @return fresh top-level activity handle | ||
389 | */ | ||
390 | struct TopLevelActivity * | ||
391 | GNUNET_FS_make_top (struct GNUNET_FS_Handle *h, | ||
392 | SuspendSignalFunction ssf, | ||
393 | void *ssf_cls) | ||
394 | { | ||
395 | struct TopLevelActivity *ret; | ||
396 | |||
397 | ret = GNUNET_new (struct TopLevelActivity); | ||
398 | ret->ssf = ssf; | ||
399 | ret->ssf_cls = ssf_cls; | ||
400 | GNUNET_CONTAINER_DLL_insert (h->top_head, h->top_tail, ret); | ||
401 | return ret; | ||
402 | } | ||
403 | |||
404 | |||
405 | /** | ||
406 | * Destroy a top-level activity entry. | ||
407 | * | ||
408 | * @param h global fs handle | ||
409 | * @param top top level activity entry | ||
410 | */ | ||
411 | void | ||
412 | GNUNET_FS_end_top (struct GNUNET_FS_Handle *h, struct TopLevelActivity *top) | ||
413 | { | ||
414 | GNUNET_CONTAINER_DLL_remove (h->top_head, h->top_tail, top); | ||
415 | GNUNET_free (top); | ||
416 | } | ||
417 | |||
418 | |||
419 | /** | ||
420 | * Closure for #GNUNET_FS_data_reader_file_(). | ||
421 | */ | ||
422 | struct FileInfo | ||
423 | { | ||
424 | /** | ||
425 | * Name of the file to read. | ||
426 | */ | ||
427 | char *filename; | ||
428 | |||
429 | /** | ||
430 | * File descriptor, NULL if it has not yet been opened. | ||
431 | */ | ||
432 | struct GNUNET_DISK_FileHandle *fd; | ||
433 | }; | ||
434 | |||
435 | |||
436 | /** | ||
437 | * Function that provides data by reading from a file. | ||
438 | * | ||
439 | * @param cls closure with the `struct FileInfo *` | ||
440 | * @param offset offset to read from; it is possible | ||
441 | * that the caller might need to go backwards | ||
442 | * a bit at times; set to `UINT64_MAX` to tell | ||
443 | * the reader that we won't be reading for a while | ||
444 | * (used to close the file descriptor but NOT fully | ||
445 | * clean up the reader's state); in this case, | ||
446 | * a value of '0' for @a max should be ignored | ||
447 | * @param max maximum number of bytes that should be | ||
448 | * copied to @a buf; readers are not allowed | ||
449 | * to provide less data unless there is an error; | ||
450 | * a value of "0" will be used at the end to allow | ||
451 | * the reader to clean up its internal state | ||
452 | * @param buf where the reader should write the data | ||
453 | * @param emsg location for the reader to store an error message | ||
454 | * @return number of bytes written, usually @a max, 0 on error | ||
455 | */ | ||
456 | size_t | ||
457 | GNUNET_FS_data_reader_file_ (void *cls, | ||
458 | uint64_t offset, | ||
459 | size_t max, | ||
460 | void *buf, | ||
461 | char **emsg) | ||
462 | { | ||
463 | struct FileInfo *fi = cls; | ||
464 | ssize_t ret; | ||
465 | |||
466 | if (UINT64_MAX == offset) | ||
467 | { | ||
468 | if (NULL != fi->fd) | ||
469 | { | ||
470 | GNUNET_DISK_file_close (fi->fd); | ||
471 | fi->fd = NULL; | ||
472 | } | ||
473 | return 0; | ||
474 | } | ||
475 | if (0 == max) | ||
476 | { | ||
477 | if (NULL != fi->fd) | ||
478 | GNUNET_DISK_file_close (fi->fd); | ||
479 | GNUNET_free (fi->filename); | ||
480 | GNUNET_free (fi); | ||
481 | return 0; | ||
482 | } | ||
483 | if (NULL == fi->fd) | ||
484 | { | ||
485 | fi->fd = GNUNET_DISK_file_open (fi->filename, | ||
486 | GNUNET_DISK_OPEN_READ, | ||
487 | GNUNET_DISK_PERM_NONE); | ||
488 | if (NULL == fi->fd) | ||
489 | { | ||
490 | GNUNET_asprintf (emsg, | ||
491 | _ ("Could not open file `%s': %s"), | ||
492 | fi->filename, | ||
493 | strerror (errno)); | ||
494 | return 0; | ||
495 | } | ||
496 | } | ||
497 | if ((GNUNET_SYSERR == | ||
498 | GNUNET_DISK_file_seek (fi->fd, offset, GNUNET_DISK_SEEK_SET)) || | ||
499 | (-1 == (ret = GNUNET_DISK_file_read (fi->fd, buf, max)))) | ||
500 | { | ||
501 | GNUNET_asprintf (emsg, | ||
502 | _ ("Could not read file `%s': %s"), | ||
503 | fi->filename, | ||
504 | strerror (errno)); | ||
505 | return 0; | ||
506 | } | ||
507 | if (ret != max) | ||
508 | { | ||
509 | GNUNET_asprintf (emsg, | ||
510 | _ ("Short read reading from file `%s'!"), | ||
511 | fi->filename); | ||
512 | return 0; | ||
513 | } | ||
514 | return max; | ||
515 | } | ||
516 | |||
517 | |||
518 | /** | ||
519 | * Create the closure for the #GNUNET_FS_data_reader_file_() callback. | ||
520 | * | ||
521 | * @param filename file to read | ||
522 | * @return closure to use, NULL on error | ||
523 | */ | ||
524 | void * | ||
525 | GNUNET_FS_make_file_reader_context_ (const char *filename) | ||
526 | { | ||
527 | struct FileInfo *fi; | ||
528 | |||
529 | fi = GNUNET_new (struct FileInfo); | ||
530 | fi->filename = GNUNET_STRINGS_filename_expand (filename); | ||
531 | if (NULL == fi->filename) | ||
532 | { | ||
533 | GNUNET_free (fi); | ||
534 | return NULL; | ||
535 | } | ||
536 | return fi; | ||
537 | } | ||
538 | |||
539 | |||
540 | /** | ||
541 | * Function that provides data by copying from a buffer. | ||
542 | * | ||
543 | * @param cls closure (points to the buffer) | ||
544 | * @param offset offset to read from; it is possible | ||
545 | * that the caller might need to go backwards | ||
546 | * a bit at times; set to `UINT64_MAX` to tell | ||
547 | * the reader that we won't be reading for a while | ||
548 | * (used to close the file descriptor but NOT fully | ||
549 | * clean up the reader's state); in this case, | ||
550 | * a value of '0' for @a max should be ignored | ||
551 | * @param max maximum number of bytes that should be | ||
552 | * copied to @a buf; readers are not allowed | ||
553 | * to provide less data unless there is an error; | ||
554 | * a value of "0" will be used at the end to allow | ||
555 | * the reader to clean up its internal state | ||
556 | * @param buf where the reader should write the data | ||
557 | * @param emsg location for the reader to store an error message | ||
558 | * @return number of bytes written, usually @a max, 0 on error | ||
559 | */ | ||
560 | size_t | ||
561 | GNUNET_FS_data_reader_copy_ (void *cls, | ||
562 | uint64_t offset, | ||
563 | size_t max, | ||
564 | void *buf, | ||
565 | char **emsg) | ||
566 | { | ||
567 | char *data = cls; | ||
568 | |||
569 | if (UINT64_MAX == offset) | ||
570 | return 0; | ||
571 | if (0 == max) | ||
572 | { | ||
573 | GNUNET_free (data); | ||
574 | return 0; | ||
575 | } | ||
576 | GNUNET_memcpy (buf, &data[offset], max); | ||
577 | return max; | ||
578 | } | ||
579 | |||
580 | |||
581 | /** | ||
582 | * Return the full filename where we would store state information | ||
583 | * (for serialization/deserialization). | ||
584 | * | ||
585 | * @param h master context | ||
586 | * @param ext component of the path | ||
587 | * @param ent entity identifier (or empty string for the directory) | ||
588 | * @return NULL on error | ||
589 | */ | ||
590 | static char * | ||
591 | get_serialization_file_name (struct GNUNET_FS_Handle *h, | ||
592 | const char *ext, | ||
593 | const char *ent) | ||
594 | { | ||
595 | char *basename; | ||
596 | char *ret; | ||
597 | |||
598 | if (0 == (h->flags & GNUNET_FS_FLAGS_PERSISTENCE)) | ||
599 | return NULL; /* persistence not requested */ | ||
600 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (h->cfg, | ||
601 | "fs", | ||
602 | "STATE_DIR", | ||
603 | &basename)) | ||
604 | return NULL; | ||
605 | GNUNET_asprintf (&ret, | ||
606 | "%s%s%s%s%s%s%s", | ||
607 | basename, | ||
608 | DIR_SEPARATOR_STR, | ||
609 | h->client_name, | ||
610 | DIR_SEPARATOR_STR, | ||
611 | ext, | ||
612 | DIR_SEPARATOR_STR, | ||
613 | ent); | ||
614 | GNUNET_free (basename); | ||
615 | return ret; | ||
616 | } | ||
617 | |||
618 | |||
619 | /** | ||
620 | * Return the full filename where we would store state information | ||
621 | * (for serialization/deserialization) that is associated with a | ||
622 | * parent operation. | ||
623 | * | ||
624 | * @param h master context | ||
625 | * @param ext component of the path | ||
626 | * @param uni name of the parent operation | ||
627 | * @param ent entity identifier (or empty string for the directory) | ||
628 | * @return NULL on error | ||
629 | */ | ||
630 | static char * | ||
631 | get_serialization_file_name_in_dir (struct GNUNET_FS_Handle *h, | ||
632 | const char *ext, | ||
633 | const char *uni, | ||
634 | const char *ent) | ||
635 | { | ||
636 | char *basename; | ||
637 | char *ret; | ||
638 | |||
639 | if (0 == (h->flags & GNUNET_FS_FLAGS_PERSISTENCE)) | ||
640 | return NULL; /* persistence not requested */ | ||
641 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (h->cfg, | ||
642 | "fs", | ||
643 | "STATE_DIR", | ||
644 | &basename)) | ||
645 | return NULL; | ||
646 | GNUNET_asprintf (&ret, | ||
647 | "%s%s%s%s%s%s%s.dir%s%s", | ||
648 | basename, | ||
649 | DIR_SEPARATOR_STR, | ||
650 | h->client_name, | ||
651 | DIR_SEPARATOR_STR, | ||
652 | ext, | ||
653 | DIR_SEPARATOR_STR, | ||
654 | uni, | ||
655 | DIR_SEPARATOR_STR, | ||
656 | ent); | ||
657 | GNUNET_free (basename); | ||
658 | return ret; | ||
659 | } | ||
660 | |||
661 | |||
662 | /** | ||
663 | * Return a read handle for deserialization. | ||
664 | * | ||
665 | * @param h master context | ||
666 | * @param ext component of the path | ||
667 | * @param ent entity identifier (or empty string for the directory) | ||
668 | * @return NULL on error | ||
669 | */ | ||
670 | static struct GNUNET_BIO_ReadHandle * | ||
671 | get_read_handle (struct GNUNET_FS_Handle *h, const char *ext, const char *ent) | ||
672 | { | ||
673 | char *fn; | ||
674 | struct GNUNET_BIO_ReadHandle *ret; | ||
675 | |||
676 | fn = get_serialization_file_name (h, ext, ent); | ||
677 | if (NULL == fn) | ||
678 | return NULL; | ||
679 | ret = GNUNET_BIO_read_open_file (fn); | ||
680 | GNUNET_free (fn); | ||
681 | return ret; | ||
682 | } | ||
683 | |||
684 | |||
685 | /** | ||
686 | * Return a write handle for serialization. | ||
687 | * | ||
688 | * @param h master context | ||
689 | * @param ext component of the path | ||
690 | * @param ent entity identifier (or empty string for the directory) | ||
691 | * @return NULL on error | ||
692 | */ | ||
693 | static struct GNUNET_BIO_WriteHandle * | ||
694 | get_write_handle (struct GNUNET_FS_Handle *h, const char *ext, const char *ent) | ||
695 | { | ||
696 | char *fn; | ||
697 | struct GNUNET_BIO_WriteHandle *ret; | ||
698 | |||
699 | fn = get_serialization_file_name (h, ext, ent); | ||
700 | if (NULL == fn) | ||
701 | return NULL; | ||
702 | ret = GNUNET_BIO_write_open_file (fn); | ||
703 | GNUNET_break (NULL != ret); | ||
704 | GNUNET_free (fn); | ||
705 | return ret; | ||
706 | } | ||
707 | |||
708 | |||
709 | /** | ||
710 | * Return a write handle for serialization. | ||
711 | * | ||
712 | * @param h master context | ||
713 | * @param ext component of the path | ||
714 | * @param uni name of parent | ||
715 | * @param ent entity identifier (or empty string for the directory) | ||
716 | * @return NULL on error | ||
717 | */ | ||
718 | static struct GNUNET_BIO_WriteHandle * | ||
719 | get_write_handle_in_dir (struct GNUNET_FS_Handle *h, | ||
720 | const char *ext, | ||
721 | const char *uni, | ||
722 | const char *ent) | ||
723 | { | ||
724 | char *fn; | ||
725 | struct GNUNET_BIO_WriteHandle *ret; | ||
726 | |||
727 | fn = get_serialization_file_name_in_dir (h, ext, uni, ent); | ||
728 | if (NULL == fn) | ||
729 | return NULL; | ||
730 | ret = GNUNET_BIO_write_open_file (fn); | ||
731 | GNUNET_free (fn); | ||
732 | return ret; | ||
733 | } | ||
734 | |||
735 | |||
736 | /** | ||
737 | * Remove serialization/deserialization file from disk. | ||
738 | * | ||
739 | * @param h master context | ||
740 | * @param ext component of the path | ||
741 | * @param ent entity identifier | ||
742 | */ | ||
743 | void | ||
744 | GNUNET_FS_remove_sync_file_ (struct GNUNET_FS_Handle *h, | ||
745 | const char *ext, | ||
746 | const char *ent) | ||
747 | { | ||
748 | char *filename; | ||
749 | |||
750 | if ((NULL == ent) || (0 == strlen (ent))) | ||
751 | { | ||
752 | GNUNET_break (0); | ||
753 | return; | ||
754 | } | ||
755 | filename = get_serialization_file_name (h, ext, ent); | ||
756 | if (NULL != filename) | ||
757 | { | ||
758 | if ((0 != unlink (filename)) && (ENOENT != errno)) | ||
759 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", filename); | ||
760 | GNUNET_free (filename); | ||
761 | } | ||
762 | } | ||
763 | |||
764 | |||
765 | /** | ||
766 | * Remove serialization/deserialization file from disk. | ||
767 | * | ||
768 | * @param h master context | ||
769 | * @param ext component of the path | ||
770 | * @param uni parent name | ||
771 | * @param ent entity identifier | ||
772 | */ | ||
773 | static void | ||
774 | remove_sync_file_in_dir (struct GNUNET_FS_Handle *h, | ||
775 | const char *ext, | ||
776 | const char *uni, | ||
777 | const char *ent) | ||
778 | { | ||
779 | char *filename; | ||
780 | |||
781 | if ((NULL == ent) || (0 == strlen (ent))) | ||
782 | { | ||
783 | GNUNET_break (0); | ||
784 | return; | ||
785 | } | ||
786 | filename = get_serialization_file_name_in_dir (h, ext, uni, ent); | ||
787 | if (NULL == filename) | ||
788 | return; | ||
789 | if (0 != unlink (filename)) | ||
790 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", filename); | ||
791 | GNUNET_free (filename); | ||
792 | } | ||
793 | |||
794 | |||
795 | /** | ||
796 | * Remove serialization/deserialization directory from disk. | ||
797 | * | ||
798 | * @param h master context | ||
799 | * @param ext component of the path | ||
800 | * @param uni unique name of parent | ||
801 | */ | ||
802 | void | ||
803 | GNUNET_FS_remove_sync_dir_ (struct GNUNET_FS_Handle *h, | ||
804 | const char *ext, | ||
805 | const char *uni) | ||
806 | { | ||
807 | char *dn; | ||
808 | |||
809 | if (NULL == uni) | ||
810 | return; | ||
811 | dn = get_serialization_file_name_in_dir (h, ext, uni, ""); | ||
812 | if (NULL == dn) | ||
813 | return; | ||
814 | if ((GNUNET_YES == GNUNET_DISK_directory_test (dn, GNUNET_YES)) && | ||
815 | (GNUNET_OK != GNUNET_DISK_directory_remove (dn))) | ||
816 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", dn); | ||
817 | GNUNET_free (dn); | ||
818 | } | ||
819 | |||
820 | |||
821 | /** | ||
822 | * Serialize a start-time. Since we use start-times to | ||
823 | * calculate the duration of some operation, we actually | ||
824 | * do not serialize the absolute time but the (relative) | ||
825 | * duration since the start time. When we then | ||
826 | * deserialize the start time, we take the current time and | ||
827 | * subtract that duration so that we get again an absolute | ||
828 | * time stamp that will result in correct performance | ||
829 | * calculations. | ||
830 | * | ||
831 | * @param wh handle for writing | ||
832 | * @param timestamp time to serialize | ||
833 | * @return #GNUNET_OK on success | ||
834 | */ | ||
835 | static int | ||
836 | write_start_time (struct GNUNET_BIO_WriteHandle *wh, | ||
837 | struct GNUNET_TIME_Absolute timestamp) | ||
838 | { | ||
839 | struct GNUNET_TIME_Relative dur; | ||
840 | |||
841 | dur = GNUNET_TIME_absolute_get_duration (timestamp); | ||
842 | return GNUNET_BIO_write_int64 (wh, "start time", dur.rel_value_us); | ||
843 | } | ||
844 | |||
845 | |||
846 | /** | ||
847 | * Deserialize a start-time. Since we use start-times to | ||
848 | * calculate the duration of some operation, we actually | ||
849 | * do not serialize the absolute time but the (relative) | ||
850 | * duration since the start time. Thus, when we then | ||
851 | * deserialize the start time, we take the current time and | ||
852 | * subtract that duration so that we get again an absolute | ||
853 | * time stamp that will result in correct performance | ||
854 | * calculations. | ||
855 | * | ||
856 | * @param rh handle for reading | ||
857 | * @param timestamp where to write the deserialized timestamp | ||
858 | * @return #GNUNET_OK on success | ||
859 | */ | ||
860 | static int | ||
861 | read_start_time (struct GNUNET_BIO_ReadHandle *rh, | ||
862 | struct GNUNET_TIME_Absolute *timestamp) | ||
863 | { | ||
864 | struct GNUNET_TIME_Relative dur; | ||
865 | |||
866 | if (GNUNET_OK != GNUNET_BIO_read_int64 (rh, "start time", | ||
867 | (int64_t *) &dur.rel_value_us)) | ||
868 | return GNUNET_SYSERR; | ||
869 | *timestamp = GNUNET_TIME_absolute_subtract (GNUNET_TIME_absolute_get (), dur); | ||
870 | return GNUNET_OK; | ||
871 | } | ||
872 | |||
873 | |||
874 | /** | ||
875 | * Using the given serialization filename, try to deserialize | ||
876 | * the file-information tree associated with it. | ||
877 | * | ||
878 | * @param h master context | ||
879 | * @param filename name of the file (without directory) with | ||
880 | * the information | ||
881 | * @return NULL on error | ||
882 | */ | ||
883 | static struct GNUNET_FS_FileInformation * | ||
884 | deserialize_file_information (struct GNUNET_FS_Handle *h, const char *filename); | ||
885 | |||
886 | |||
887 | /** | ||
888 | * Using the given serialization filename, try to deserialize | ||
889 | * the file-information tree associated with it. | ||
890 | * | ||
891 | * @param h master context | ||
892 | * @param fn name of the file (without directory) with | ||
893 | * the information | ||
894 | * @param rh handle for reading | ||
895 | * @return NULL on error | ||
896 | */ | ||
897 | static struct GNUNET_FS_FileInformation * | ||
898 | deserialize_fi_node (struct GNUNET_FS_Handle *h, | ||
899 | const char *fn, | ||
900 | struct GNUNET_BIO_ReadHandle *rh) | ||
901 | { | ||
902 | struct GNUNET_FS_FileInformation *ret; | ||
903 | struct GNUNET_FS_FileInformation *nxt; | ||
904 | char b; | ||
905 | char *ksks; | ||
906 | char *chks; | ||
907 | char *skss; | ||
908 | char *filename; | ||
909 | uint32_t dsize; | ||
910 | |||
911 | if (GNUNET_OK != GNUNET_BIO_read (rh, "status flag", &b, sizeof(b))) | ||
912 | { | ||
913 | GNUNET_break (0); | ||
914 | return NULL; | ||
915 | } | ||
916 | ret = GNUNET_new (struct GNUNET_FS_FileInformation); | ||
917 | ret->h = h; | ||
918 | ksks = NULL; | ||
919 | chks = NULL; | ||
920 | skss = NULL; | ||
921 | filename = NULL; | ||
922 | if ((GNUNET_OK != GNUNET_BIO_read_meta_data (rh, "metadata", &ret->meta)) || | ||
923 | (GNUNET_OK != GNUNET_BIO_read_string (rh, "ksk-uri", &ksks, 32 * 1024)) || | ||
924 | ((NULL != ksks) && | ||
925 | ((NULL == (ret->keywords = GNUNET_FS_uri_parse (ksks, NULL))) || | ||
926 | (GNUNET_YES != GNUNET_FS_uri_test_ksk (ret->keywords)))) || | ||
927 | (GNUNET_OK != GNUNET_BIO_read_string (rh, "chk-uri", &chks, 1024)) || | ||
928 | ((NULL != chks) && | ||
929 | ((NULL == (ret->chk_uri = GNUNET_FS_uri_parse (chks, NULL))) || | ||
930 | (GNUNET_YES != GNUNET_FS_uri_test_chk (ret->chk_uri)))) || | ||
931 | (GNUNET_OK != GNUNET_BIO_read_string (rh, "sks-uri", &skss, 1024)) || | ||
932 | ((NULL != skss) && | ||
933 | ((NULL == (ret->sks_uri = GNUNET_FS_uri_parse (skss, NULL))) || | ||
934 | (GNUNET_YES != GNUNET_FS_uri_test_sks (ret->sks_uri)))) || | ||
935 | (GNUNET_OK != read_start_time (rh, &ret->start_time)) || | ||
936 | (GNUNET_OK != | ||
937 | GNUNET_BIO_read_string (rh, "emsg", &ret->emsg, 16 * 1024)) || | ||
938 | (GNUNET_OK != | ||
939 | GNUNET_BIO_read_string (rh, "fn", &ret->filename, 16 * 1024)) || | ||
940 | (GNUNET_OK != | ||
941 | GNUNET_BIO_read_int64 ( | ||
942 | rh, | ||
943 | "expiration time", | ||
944 | (int64_t *) &ret->bo.expiration_time.abs_value_us)) || | ||
945 | (GNUNET_OK != GNUNET_BIO_read_int32 ( | ||
946 | rh, | ||
947 | "anonymity level", | ||
948 | (int32_t *) &ret->bo.anonymity_level)) || | ||
949 | (GNUNET_OK != GNUNET_BIO_read_int32 ( | ||
950 | rh, | ||
951 | "content priority", | ||
952 | (int32_t *) &ret->bo.content_priority)) || | ||
953 | (GNUNET_OK != GNUNET_BIO_read_int32 ( | ||
954 | rh, | ||
955 | "replication level", | ||
956 | (int32_t *) &ret->bo.replication_level))) | ||
957 | { | ||
958 | GNUNET_break (0); | ||
959 | goto cleanup; | ||
960 | } | ||
961 | switch (b) | ||
962 | { | ||
963 | case 0: /* file-insert */ | ||
964 | if (GNUNET_OK != GNUNET_BIO_read_int64 ( | ||
965 | rh, | ||
966 | "file size", | ||
967 | (int64_t *) &ret->data.file.file_size)) | ||
968 | { | ||
969 | GNUNET_break (0); | ||
970 | goto cleanup; | ||
971 | } | ||
972 | ret->is_directory = GNUNET_NO; | ||
973 | ret->data.file.do_index = GNUNET_NO; | ||
974 | ret->data.file.have_hash = GNUNET_NO; | ||
975 | ret->data.file.index_start_confirmed = GNUNET_NO; | ||
976 | if (GNUNET_NO == ret->is_published) | ||
977 | { | ||
978 | if (NULL == ret->filename) | ||
979 | { | ||
980 | ret->data.file.reader = &GNUNET_FS_data_reader_copy_; | ||
981 | ret->data.file.reader_cls = | ||
982 | GNUNET_malloc_large (ret->data.file.file_size); | ||
983 | if (ret->data.file.reader_cls == NULL) | ||
984 | goto cleanup; | ||
985 | if (GNUNET_OK != GNUNET_BIO_read (rh, | ||
986 | "file-data", | ||
987 | ret->data.file.reader_cls, | ||
988 | ret->data.file.file_size)) | ||
989 | { | ||
990 | GNUNET_break (0); | ||
991 | goto cleanup; | ||
992 | } | ||
993 | } | ||
994 | else | ||
995 | { | ||
996 | ret->data.file.reader = &GNUNET_FS_data_reader_file_; | ||
997 | ret->data.file.reader_cls = | ||
998 | GNUNET_FS_make_file_reader_context_ (ret->filename); | ||
999 | } | ||
1000 | } | ||
1001 | break; | ||
1002 | |||
1003 | case 1: /* file-index, no hash */ | ||
1004 | if (NULL == ret->filename) | ||
1005 | { | ||
1006 | GNUNET_break (0); | ||
1007 | goto cleanup; | ||
1008 | } | ||
1009 | if (GNUNET_OK != GNUNET_BIO_read_int64 ( | ||
1010 | rh, | ||
1011 | "file size", | ||
1012 | (int64_t *) &ret->data.file.file_size)) | ||
1013 | { | ||
1014 | GNUNET_break (0); | ||
1015 | goto cleanup; | ||
1016 | } | ||
1017 | ret->is_directory = GNUNET_NO; | ||
1018 | ret->data.file.do_index = GNUNET_YES; | ||
1019 | ret->data.file.have_hash = GNUNET_NO; | ||
1020 | ret->data.file.index_start_confirmed = GNUNET_NO; | ||
1021 | ret->data.file.reader = &GNUNET_FS_data_reader_file_; | ||
1022 | ret->data.file.reader_cls = | ||
1023 | GNUNET_FS_make_file_reader_context_ (ret->filename); | ||
1024 | break; | ||
1025 | |||
1026 | case 2: /* file-index-with-hash */ | ||
1027 | if (NULL == ret->filename) | ||
1028 | { | ||
1029 | GNUNET_break (0); | ||
1030 | goto cleanup; | ||
1031 | } | ||
1032 | if ((GNUNET_OK != GNUNET_BIO_read_int64 ( | ||
1033 | rh, | ||
1034 | "file size", | ||
1035 | (int64_t *) &ret->data.file.file_size)) || | ||
1036 | (GNUNET_OK != GNUNET_BIO_read (rh, | ||
1037 | "fileid", | ||
1038 | &ret->data.file.file_id, | ||
1039 | sizeof(struct GNUNET_HashCode)))) | ||
1040 | { | ||
1041 | GNUNET_break (0); | ||
1042 | goto cleanup; | ||
1043 | } | ||
1044 | ret->is_directory = GNUNET_NO; | ||
1045 | ret->data.file.do_index = GNUNET_YES; | ||
1046 | ret->data.file.have_hash = GNUNET_YES; | ||
1047 | ret->data.file.index_start_confirmed = GNUNET_NO; | ||
1048 | ret->data.file.reader = &GNUNET_FS_data_reader_file_; | ||
1049 | ret->data.file.reader_cls = | ||
1050 | GNUNET_FS_make_file_reader_context_ (ret->filename); | ||
1051 | break; | ||
1052 | |||
1053 | case 3: /* file-index-with-hash-confirmed */ | ||
1054 | if (NULL == ret->filename) | ||
1055 | { | ||
1056 | GNUNET_break (0); | ||
1057 | goto cleanup; | ||
1058 | } | ||
1059 | if ((GNUNET_OK != GNUNET_BIO_read_int64 ( | ||
1060 | rh, | ||
1061 | "file size", | ||
1062 | (int64_t *) &ret->data.file.file_size)) || | ||
1063 | (GNUNET_OK != GNUNET_BIO_read (rh, | ||
1064 | "fileid", | ||
1065 | &ret->data.file.file_id, | ||
1066 | sizeof(struct GNUNET_HashCode)))) | ||
1067 | { | ||
1068 | GNUNET_break (0); | ||
1069 | goto cleanup; | ||
1070 | } | ||
1071 | ret->is_directory = GNUNET_NO; | ||
1072 | ret->data.file.do_index = GNUNET_YES; | ||
1073 | ret->data.file.have_hash = GNUNET_YES; | ||
1074 | ret->data.file.index_start_confirmed = GNUNET_YES; | ||
1075 | ret->data.file.reader = &GNUNET_FS_data_reader_file_; | ||
1076 | ret->data.file.reader_cls = | ||
1077 | GNUNET_FS_make_file_reader_context_ (ret->filename); | ||
1078 | break; | ||
1079 | |||
1080 | case 4: /* directory */ | ||
1081 | ret->is_directory = GNUNET_YES; | ||
1082 | if ((GNUNET_OK != GNUNET_BIO_read_int32 (rh, "dsize", | ||
1083 | (int32_t *) &dsize)) || | ||
1084 | (GNUNET_OK != | ||
1085 | GNUNET_BIO_read_int64 ( | ||
1086 | rh, | ||
1087 | "contents completed", | ||
1088 | (int64_t *) &ret->data.dir.contents_completed)) || | ||
1089 | (GNUNET_OK != | ||
1090 | GNUNET_BIO_read_int64 ( | ||
1091 | rh, | ||
1092 | "contents size", | ||
1093 | (int64_t *) &ret->data.dir.contents_size)) || | ||
1094 | (NULL == (ret->data.dir.dir_data = GNUNET_malloc_large (dsize))) || | ||
1095 | (GNUNET_OK != | ||
1096 | GNUNET_BIO_read (rh, "dir-data", ret->data.dir.dir_data, dsize)) || | ||
1097 | (GNUNET_OK != | ||
1098 | GNUNET_BIO_read_string (rh, "ent-filename", &filename, 16 * 1024))) | ||
1099 | { | ||
1100 | GNUNET_break (0); | ||
1101 | goto cleanup; | ||
1102 | } | ||
1103 | ret->data.dir.dir_size = (uint32_t) dsize; | ||
1104 | if (NULL != filename) | ||
1105 | { | ||
1106 | ret->data.dir.entries = deserialize_file_information (h, filename); | ||
1107 | GNUNET_free (filename); | ||
1108 | filename = NULL; | ||
1109 | nxt = ret->data.dir.entries; | ||
1110 | while (NULL != nxt) | ||
1111 | { | ||
1112 | nxt->dir = ret; | ||
1113 | nxt = nxt->next; | ||
1114 | } | ||
1115 | } | ||
1116 | break; | ||
1117 | |||
1118 | default: | ||
1119 | GNUNET_break (0); | ||
1120 | goto cleanup; | ||
1121 | } | ||
1122 | ret->serialization = GNUNET_strdup (fn); | ||
1123 | if (GNUNET_OK != | ||
1124 | GNUNET_BIO_read_string (rh, "nxt-filename", &filename, 16 * 1024)) | ||
1125 | { | ||
1126 | GNUNET_break (0); | ||
1127 | goto cleanup; | ||
1128 | } | ||
1129 | if (NULL != filename) | ||
1130 | { | ||
1131 | ret->next = deserialize_file_information (h, filename); | ||
1132 | GNUNET_free (filename); | ||
1133 | filename = NULL; | ||
1134 | } | ||
1135 | GNUNET_free (ksks); | ||
1136 | GNUNET_free (skss); | ||
1137 | GNUNET_free (chks); | ||
1138 | return ret; | ||
1139 | cleanup: | ||
1140 | GNUNET_free (ksks); | ||
1141 | GNUNET_free (chks); | ||
1142 | GNUNET_free (skss); | ||
1143 | GNUNET_free (filename); | ||
1144 | GNUNET_FS_file_information_destroy (ret, NULL, NULL); | ||
1145 | return NULL; | ||
1146 | } | ||
1147 | |||
1148 | |||
1149 | /** | ||
1150 | * Using the given serialization filename, try to deserialize | ||
1151 | * the file-information tree associated with it. | ||
1152 | * | ||
1153 | * @param h master context | ||
1154 | * @param filename name of the file (without directory) with | ||
1155 | * the information | ||
1156 | * @return NULL on error | ||
1157 | */ | ||
1158 | static struct GNUNET_FS_FileInformation * | ||
1159 | deserialize_file_information (struct GNUNET_FS_Handle *h, const char *filename) | ||
1160 | { | ||
1161 | struct GNUNET_FS_FileInformation *ret; | ||
1162 | struct GNUNET_BIO_ReadHandle *rh; | ||
1163 | char *emsg; | ||
1164 | char *fn; | ||
1165 | |||
1166 | rh = get_read_handle (h, GNUNET_FS_SYNC_PATH_FILE_INFO, filename); | ||
1167 | if (NULL == rh) | ||
1168 | return NULL; | ||
1169 | ret = deserialize_fi_node (h, filename, rh); | ||
1170 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
1171 | { | ||
1172 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1173 | _ ("Failed to resume publishing information `%s': %s\n"), | ||
1174 | filename, | ||
1175 | emsg); | ||
1176 | GNUNET_free (emsg); | ||
1177 | } | ||
1178 | if (NULL == ret) | ||
1179 | { | ||
1180 | fn = | ||
1181 | get_serialization_file_name (h, GNUNET_FS_SYNC_PATH_FILE_INFO, filename); | ||
1182 | if (NULL != fn) | ||
1183 | { | ||
1184 | if (0 != unlink (fn)) | ||
1185 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn); | ||
1186 | GNUNET_free (fn); | ||
1187 | } | ||
1188 | } | ||
1189 | return ret; | ||
1190 | } | ||
1191 | |||
1192 | |||
1193 | /** | ||
1194 | * Given a serialization name (full absolute path), return the | ||
1195 | * basename of the file (without the path), which must only | ||
1196 | * consist of the 6 random characters. | ||
1197 | * | ||
1198 | * @param fullname name to extract the basename from | ||
1199 | * @return copy of the basename, NULL on error | ||
1200 | */ | ||
1201 | static char * | ||
1202 | get_serialization_short_name (const char *fullname) | ||
1203 | { | ||
1204 | const char *end; | ||
1205 | const char *nxt; | ||
1206 | |||
1207 | end = NULL; | ||
1208 | nxt = fullname; | ||
1209 | /* FIXME: we could do this faster since we know | ||
1210 | * the length of 'end'... */ | ||
1211 | while ('\0' != *nxt) | ||
1212 | { | ||
1213 | if (DIR_SEPARATOR == *nxt) | ||
1214 | end = nxt + 1; | ||
1215 | nxt++; | ||
1216 | } | ||
1217 | if ((NULL == end) || (0 == strlen (end))) | ||
1218 | { | ||
1219 | GNUNET_break (0); | ||
1220 | return NULL; | ||
1221 | } | ||
1222 | GNUNET_break (6 == strlen (end)); | ||
1223 | return GNUNET_strdup (end); | ||
1224 | } | ||
1225 | |||
1226 | |||
1227 | /** | ||
1228 | * Create a new random name for serialization. Also checks if persistence | ||
1229 | * is enabled and returns NULL if not. | ||
1230 | * | ||
1231 | * @param h master context | ||
1232 | * @param ext component of the path | ||
1233 | * @return NULL on error | ||
1234 | */ | ||
1235 | static char * | ||
1236 | make_serialization_file_name (struct GNUNET_FS_Handle *h, const char *ext) | ||
1237 | { | ||
1238 | char *fn; | ||
1239 | char *dn; | ||
1240 | char *ret; | ||
1241 | |||
1242 | if (0 == (h->flags & GNUNET_FS_FLAGS_PERSISTENCE)) | ||
1243 | return NULL; /* persistence not requested */ | ||
1244 | dn = get_serialization_file_name (h, ext, ""); | ||
1245 | if (NULL == dn) | ||
1246 | return NULL; | ||
1247 | if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (dn)) | ||
1248 | { | ||
1249 | GNUNET_free (dn); | ||
1250 | return NULL; | ||
1251 | } | ||
1252 | fn = GNUNET_DISK_mktemp (dn); | ||
1253 | GNUNET_free (dn); | ||
1254 | if (NULL == fn) | ||
1255 | return NULL; /* epic fail */ | ||
1256 | ret = get_serialization_short_name (fn); | ||
1257 | GNUNET_free (fn); | ||
1258 | return ret; | ||
1259 | } | ||
1260 | |||
1261 | |||
1262 | /** | ||
1263 | * Create a new random name for serialization. Also checks if persistence | ||
1264 | * is enabled and returns NULL if not. | ||
1265 | * | ||
1266 | * @param h master context | ||
1267 | * @param ext component of the path | ||
1268 | * @param uni name of parent | ||
1269 | * @return NULL on error | ||
1270 | */ | ||
1271 | static char * | ||
1272 | make_serialization_file_name_in_dir (struct GNUNET_FS_Handle *h, | ||
1273 | const char *ext, | ||
1274 | const char *uni) | ||
1275 | { | ||
1276 | char *fn; | ||
1277 | char *dn; | ||
1278 | char *ret; | ||
1279 | |||
1280 | if (0 == (h->flags & GNUNET_FS_FLAGS_PERSISTENCE)) | ||
1281 | return NULL; /* persistence not requested */ | ||
1282 | dn = get_serialization_file_name_in_dir (h, ext, uni, ""); | ||
1283 | if (NULL == dn) | ||
1284 | return NULL; | ||
1285 | if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (dn)) | ||
1286 | { | ||
1287 | GNUNET_free (dn); | ||
1288 | return NULL; | ||
1289 | } | ||
1290 | fn = GNUNET_DISK_mktemp (dn); | ||
1291 | GNUNET_free (dn); | ||
1292 | if (NULL == fn) | ||
1293 | return NULL; /* epic fail */ | ||
1294 | ret = get_serialization_short_name (fn); | ||
1295 | GNUNET_free (fn); | ||
1296 | return ret; | ||
1297 | } | ||
1298 | |||
1299 | |||
1300 | /** | ||
1301 | * Copy all of the data from the reader to the write handle. | ||
1302 | * | ||
1303 | * @param wh write handle | ||
1304 | * @param fi file with reader | ||
1305 | * @return #GNUNET_OK on success | ||
1306 | */ | ||
1307 | static int | ||
1308 | copy_from_reader (struct GNUNET_BIO_WriteHandle *wh, | ||
1309 | struct GNUNET_FS_FileInformation *fi) | ||
1310 | { | ||
1311 | char buf[32 * 1024]; | ||
1312 | uint64_t off; | ||
1313 | size_t ret; | ||
1314 | size_t left; | ||
1315 | char *emsg; | ||
1316 | |||
1317 | emsg = NULL; | ||
1318 | off = 0; | ||
1319 | while (off < fi->data.file.file_size) | ||
1320 | { | ||
1321 | left = GNUNET_MIN (sizeof(buf), fi->data.file.file_size - off); | ||
1322 | ret = | ||
1323 | fi->data.file.reader (fi->data.file.reader_cls, off, left, buf, &emsg); | ||
1324 | if (0 == ret) | ||
1325 | { | ||
1326 | GNUNET_free (emsg); | ||
1327 | return GNUNET_SYSERR; | ||
1328 | } | ||
1329 | if (GNUNET_OK != GNUNET_BIO_write (wh, "copied from reader", buf, ret)) | ||
1330 | return GNUNET_SYSERR; | ||
1331 | off += ret; | ||
1332 | } | ||
1333 | return GNUNET_OK; | ||
1334 | } | ||
1335 | |||
1336 | |||
1337 | /** | ||
1338 | * Create a temporary file on disk to store the current | ||
1339 | * state of @a fi in. | ||
1340 | * | ||
1341 | * @param fi file information to sync with disk | ||
1342 | */ | ||
1343 | void | ||
1344 | GNUNET_FS_file_information_sync_ (struct GNUNET_FS_FileInformation *fi) | ||
1345 | { | ||
1346 | char *fn; | ||
1347 | struct GNUNET_BIO_WriteHandle *wh; | ||
1348 | char b; | ||
1349 | char *ksks; | ||
1350 | char *chks; | ||
1351 | char *skss; | ||
1352 | |||
1353 | if (NULL == fi->serialization) | ||
1354 | fi->serialization = | ||
1355 | make_serialization_file_name (fi->h, GNUNET_FS_SYNC_PATH_FILE_INFO); | ||
1356 | if (NULL == fi->serialization) | ||
1357 | return; | ||
1358 | wh = | ||
1359 | get_write_handle (fi->h, GNUNET_FS_SYNC_PATH_FILE_INFO, fi->serialization); | ||
1360 | if (NULL == wh) | ||
1361 | { | ||
1362 | GNUNET_free (fi->serialization); | ||
1363 | fi->serialization = NULL; | ||
1364 | return; | ||
1365 | } | ||
1366 | if (GNUNET_YES == fi->is_directory) | ||
1367 | b = 4; | ||
1368 | else if (GNUNET_YES == fi->data.file.index_start_confirmed) | ||
1369 | b = 3; | ||
1370 | else if (GNUNET_YES == fi->data.file.have_hash) | ||
1371 | b = 2; | ||
1372 | else if (GNUNET_YES == fi->data.file.do_index) | ||
1373 | b = 1; | ||
1374 | else | ||
1375 | b = 0; | ||
1376 | if (NULL != fi->keywords) | ||
1377 | ksks = GNUNET_FS_uri_to_string (fi->keywords); | ||
1378 | else | ||
1379 | ksks = NULL; | ||
1380 | if (NULL != fi->chk_uri) | ||
1381 | chks = GNUNET_FS_uri_to_string (fi->chk_uri); | ||
1382 | else | ||
1383 | chks = NULL; | ||
1384 | if (NULL != fi->sks_uri) | ||
1385 | skss = GNUNET_FS_uri_to_string (fi->sks_uri); | ||
1386 | else | ||
1387 | skss = NULL; | ||
1388 | struct GNUNET_BIO_WriteSpec ws1[] = { | ||
1389 | GNUNET_BIO_write_spec_object ("b", &b, sizeof (b)), | ||
1390 | GNUNET_BIO_write_spec_meta_data ("meta", fi->meta), | ||
1391 | GNUNET_BIO_write_spec_string ("ksks", ksks), | ||
1392 | GNUNET_BIO_write_spec_string ("chks", chks), | ||
1393 | GNUNET_BIO_write_spec_string ("skss", skss), | ||
1394 | GNUNET_BIO_write_spec_end (), | ||
1395 | }; | ||
1396 | struct GNUNET_BIO_WriteSpec ws2[] = { | ||
1397 | GNUNET_BIO_write_spec_string ("emsg", fi->emsg), | ||
1398 | GNUNET_BIO_write_spec_string ("filename", fi->filename), | ||
1399 | GNUNET_BIO_write_spec_int64 ( | ||
1400 | "expiration time", | ||
1401 | (int64_t *) &fi->bo.expiration_time.abs_value_us), | ||
1402 | GNUNET_BIO_write_spec_int32 ( | ||
1403 | "anonymity level", | ||
1404 | (int32_t *) &fi->bo.anonymity_level), | ||
1405 | GNUNET_BIO_write_spec_int32 ( | ||
1406 | "content priority", | ||
1407 | (int32_t *) &fi->bo.content_priority), | ||
1408 | GNUNET_BIO_write_spec_int32 ( | ||
1409 | "replication level", | ||
1410 | (int32_t *) &fi->bo.replication_level), | ||
1411 | GNUNET_BIO_write_spec_end (), | ||
1412 | }; | ||
1413 | if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws1)) || | ||
1414 | (GNUNET_OK != write_start_time (wh, fi->start_time)) || | ||
1415 | (GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws2))) | ||
1416 | { | ||
1417 | GNUNET_break (0); | ||
1418 | goto cleanup; | ||
1419 | } | ||
1420 | GNUNET_free (chks); | ||
1421 | chks = NULL; | ||
1422 | GNUNET_free (ksks); | ||
1423 | ksks = NULL; | ||
1424 | GNUNET_free (skss); | ||
1425 | skss = NULL; | ||
1426 | |||
1427 | switch (b) | ||
1428 | { | ||
1429 | case 0: /* file-insert */ | ||
1430 | if (GNUNET_OK != GNUNET_BIO_write_int64 (wh, "file size", | ||
1431 | fi->data.file.file_size)) | ||
1432 | { | ||
1433 | GNUNET_break (0); | ||
1434 | goto cleanup; | ||
1435 | } | ||
1436 | if ((GNUNET_NO == fi->is_published) && (NULL == fi->filename)) | ||
1437 | if (GNUNET_OK != copy_from_reader (wh, fi)) | ||
1438 | { | ||
1439 | GNUNET_break (0); | ||
1440 | goto cleanup; | ||
1441 | } | ||
1442 | break; | ||
1443 | |||
1444 | case 1: /* file-index, no hash */ | ||
1445 | if (NULL == fi->filename) | ||
1446 | { | ||
1447 | GNUNET_break (0); | ||
1448 | goto cleanup; | ||
1449 | } | ||
1450 | if (GNUNET_OK != GNUNET_BIO_write_int64 (wh, "file size", | ||
1451 | fi->data.file.file_size)) | ||
1452 | { | ||
1453 | GNUNET_break (0); | ||
1454 | goto cleanup; | ||
1455 | } | ||
1456 | break; | ||
1457 | |||
1458 | case 2: /* file-index-with-hash */ | ||
1459 | case 3: /* file-index-with-hash-confirmed */ | ||
1460 | if (NULL == fi->filename) | ||
1461 | { | ||
1462 | GNUNET_break (0); | ||
1463 | goto cleanup; | ||
1464 | } | ||
1465 | if ((GNUNET_OK != GNUNET_BIO_write_int64 (wh, "file size", | ||
1466 | fi->data.file.file_size)) || | ||
1467 | (GNUNET_OK != GNUNET_BIO_write (wh, | ||
1468 | "file id", | ||
1469 | &fi->data.file.file_id, | ||
1470 | sizeof(struct GNUNET_HashCode)))) | ||
1471 | { | ||
1472 | GNUNET_break (0); | ||
1473 | goto cleanup; | ||
1474 | } | ||
1475 | break; | ||
1476 | |||
1477 | case 4: /* directory */ | ||
1478 | if ((NULL != fi->data.dir.entries) && | ||
1479 | (NULL == fi->data.dir.entries->serialization)) | ||
1480 | GNUNET_FS_file_information_sync_ (fi->data.dir.entries); | ||
1481 | struct GNUNET_BIO_WriteSpec ws[] = { | ||
1482 | GNUNET_BIO_write_spec_int32 ("dir size", | ||
1483 | (int32_t *) &fi->data.dir.dir_size), | ||
1484 | GNUNET_BIO_write_spec_int64 ( | ||
1485 | "contents completed", | ||
1486 | (int64_t *) &fi->data.dir.contents_completed), | ||
1487 | GNUNET_BIO_write_spec_int64 ("contents size", | ||
1488 | (int64_t *) &fi->data.dir.contents_size), | ||
1489 | GNUNET_BIO_write_spec_object ("dir data", | ||
1490 | fi->data.dir.dir_data, | ||
1491 | (uint32_t) fi->data.dir.dir_size), | ||
1492 | GNUNET_BIO_write_spec_string ("dir entries", | ||
1493 | (fi->data.dir.entries == NULL) | ||
1494 | ? NULL | ||
1495 | : fi->data.dir.entries->serialization), | ||
1496 | GNUNET_BIO_write_spec_end (), | ||
1497 | }; | ||
1498 | if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws))) | ||
1499 | { | ||
1500 | GNUNET_break (0); | ||
1501 | goto cleanup; | ||
1502 | } | ||
1503 | break; | ||
1504 | |||
1505 | default: | ||
1506 | GNUNET_assert (0); | ||
1507 | goto cleanup; | ||
1508 | } | ||
1509 | if ((NULL != fi->next) && (NULL == fi->next->serialization)) | ||
1510 | GNUNET_FS_file_information_sync_ (fi->next); | ||
1511 | if (GNUNET_OK != GNUNET_BIO_write_string (wh, | ||
1512 | "serialization", | ||
1513 | (fi->next != NULL) | ||
1514 | ? fi->next->serialization | ||
1515 | : NULL)) | ||
1516 | { | ||
1517 | GNUNET_break (0); | ||
1518 | goto cleanup; | ||
1519 | } | ||
1520 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
1521 | { | ||
1522 | wh = NULL; | ||
1523 | GNUNET_break (0); | ||
1524 | goto cleanup; | ||
1525 | } | ||
1526 | return; /* done! */ | ||
1527 | cleanup: | ||
1528 | if (NULL != wh) | ||
1529 | (void) GNUNET_BIO_write_close (wh, NULL); | ||
1530 | GNUNET_free (chks); | ||
1531 | GNUNET_free (ksks); | ||
1532 | GNUNET_free (skss); | ||
1533 | fn = get_serialization_file_name (fi->h, | ||
1534 | GNUNET_FS_SYNC_PATH_FILE_INFO, | ||
1535 | fi->serialization); | ||
1536 | if (NULL != fn) | ||
1537 | { | ||
1538 | if (0 != unlink (fn)) | ||
1539 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn); | ||
1540 | GNUNET_free (fn); | ||
1541 | } | ||
1542 | GNUNET_free (fi->serialization); | ||
1543 | fi->serialization = NULL; | ||
1544 | } | ||
1545 | |||
1546 | |||
1547 | /** | ||
1548 | * Find the entry in the file information struct where the | ||
1549 | * serialization filename matches the given name. | ||
1550 | * | ||
1551 | * @param pos file information to search | ||
1552 | * @param srch filename to search for | ||
1553 | * @return NULL if srch was not found in this subtree | ||
1554 | */ | ||
1555 | static struct GNUNET_FS_FileInformation * | ||
1556 | find_file_position (struct GNUNET_FS_FileInformation *pos, const char *srch) | ||
1557 | { | ||
1558 | struct GNUNET_FS_FileInformation *r; | ||
1559 | |||
1560 | while (NULL != pos) | ||
1561 | { | ||
1562 | if (0 == strcmp (srch, pos->serialization)) | ||
1563 | return pos; | ||
1564 | if ((GNUNET_YES == pos->is_directory) && | ||
1565 | (NULL != (r = find_file_position (pos->data.dir.entries, srch)))) | ||
1566 | return r; | ||
1567 | pos = pos->next; | ||
1568 | } | ||
1569 | return NULL; | ||
1570 | } | ||
1571 | |||
1572 | |||
1573 | /** | ||
1574 | * Signal the FS's progress function that we are resuming | ||
1575 | * an upload. | ||
1576 | * | ||
1577 | * @param cls closure (of type `struct GNUNET_FS_PublishContext *`, for the parent (!)) | ||
1578 | * @param fi the entry in the publish-structure | ||
1579 | * @param length length of the file or directory | ||
1580 | * @param meta metadata for the file or directory (can be modified) | ||
1581 | * @param uri pointer to the keywords that will be used for this entry (can be modified) | ||
1582 | * @param bo block options (can be modified) | ||
1583 | * @param do_index should we index? | ||
1584 | * @param client_info pointer to client context set upon creation (can be modified) | ||
1585 | * @return #GNUNET_OK to continue (always) | ||
1586 | */ | ||
1587 | static int | ||
1588 | fip_signal_resume (void *cls, | ||
1589 | struct GNUNET_FS_FileInformation *fi, | ||
1590 | uint64_t length, | ||
1591 | struct GNUNET_CONTAINER_MetaData *meta, | ||
1592 | struct GNUNET_FS_Uri **uri, | ||
1593 | struct GNUNET_FS_BlockOptions *bo, | ||
1594 | int *do_index, | ||
1595 | void **client_info) | ||
1596 | { | ||
1597 | struct GNUNET_FS_PublishContext *pc = cls; | ||
1598 | struct GNUNET_FS_ProgressInfo pi; | ||
1599 | |||
1600 | if (GNUNET_YES == pc->skip_next_fi_callback) | ||
1601 | { | ||
1602 | pc->skip_next_fi_callback = GNUNET_NO; | ||
1603 | return GNUNET_OK; | ||
1604 | } | ||
1605 | pi.status = GNUNET_FS_STATUS_PUBLISH_RESUME; | ||
1606 | pi.value.publish.specifics.resume.message = fi->emsg; | ||
1607 | pi.value.publish.specifics.resume.chk_uri = fi->chk_uri; | ||
1608 | *client_info = GNUNET_FS_publish_make_status_ (&pi, pc, fi, 0); | ||
1609 | if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta)) | ||
1610 | { | ||
1611 | /* process entries in directory */ | ||
1612 | pc->skip_next_fi_callback = GNUNET_YES; | ||
1613 | GNUNET_FS_file_information_inspect (fi, &fip_signal_resume, pc); | ||
1614 | } | ||
1615 | return GNUNET_OK; | ||
1616 | } | ||
1617 | |||
1618 | |||
1619 | /** | ||
1620 | * Function called with a filename of serialized publishing operation | ||
1621 | * to deserialize. | ||
1622 | * | ||
1623 | * @param cls the `struct GNUNET_FS_Handle *` | ||
1624 | * @param filename complete filename (absolute path) | ||
1625 | * @return #GNUNET_OK (continue to iterate) | ||
1626 | */ | ||
1627 | static int | ||
1628 | deserialize_publish_file (void *cls, const char *filename) | ||
1629 | { | ||
1630 | struct GNUNET_FS_Handle *h = cls; | ||
1631 | struct GNUNET_BIO_ReadHandle *rh; | ||
1632 | struct GNUNET_FS_PublishContext *pc; | ||
1633 | int32_t options; | ||
1634 | int32_t all_done; | ||
1635 | int32_t have_ns; | ||
1636 | char *fi_root; | ||
1637 | struct GNUNET_CRYPTO_EcdsaPrivateKey ns; | ||
1638 | char *fi_pos; | ||
1639 | char *emsg; | ||
1640 | |||
1641 | pc = GNUNET_new (struct GNUNET_FS_PublishContext); | ||
1642 | pc->h = h; | ||
1643 | pc->serialization = get_serialization_short_name (filename); | ||
1644 | fi_root = NULL; | ||
1645 | fi_pos = NULL; | ||
1646 | rh = GNUNET_BIO_read_open_file (filename); | ||
1647 | if (NULL == rh) | ||
1648 | { | ||
1649 | GNUNET_break (0); | ||
1650 | goto cleanup; | ||
1651 | } | ||
1652 | struct GNUNET_BIO_ReadSpec rs[] = { | ||
1653 | GNUNET_BIO_read_spec_string ("publish-nid", &pc->nid, 1024), | ||
1654 | GNUNET_BIO_read_spec_string ("publish-nuid", &pc->nuid, 1024), | ||
1655 | GNUNET_BIO_read_spec_int32 ("options", &options), | ||
1656 | GNUNET_BIO_read_spec_int32 ("all done", &all_done), | ||
1657 | GNUNET_BIO_read_spec_int32 ("have ns", &have_ns), | ||
1658 | GNUNET_BIO_read_spec_string ("publish-firoot", &fi_root, 128), | ||
1659 | GNUNET_BIO_read_spec_string ("publish-fipos", &fi_pos, 128), | ||
1660 | GNUNET_BIO_read_spec_end (), | ||
1661 | }; | ||
1662 | if ((GNUNET_OK != GNUNET_BIO_read_spec_commit (rh, rs)) || | ||
1663 | ((GNUNET_YES == have_ns) && | ||
1664 | (GNUNET_OK != GNUNET_BIO_read (rh, "publish-ns", &ns, sizeof(ns))))) | ||
1665 | { | ||
1666 | GNUNET_break (0); | ||
1667 | goto cleanup; | ||
1668 | } | ||
1669 | pc->options = options; | ||
1670 | pc->all_done = all_done; | ||
1671 | if (NULL == fi_root) | ||
1672 | { | ||
1673 | GNUNET_break (0); | ||
1674 | goto cleanup; | ||
1675 | } | ||
1676 | pc->fi = deserialize_file_information (h, fi_root); | ||
1677 | if (NULL == pc->fi) | ||
1678 | { | ||
1679 | GNUNET_break (0); | ||
1680 | goto cleanup; | ||
1681 | } | ||
1682 | if (GNUNET_YES == have_ns) | ||
1683 | { | ||
1684 | pc->ns = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey); | ||
1685 | *pc->ns = ns; | ||
1686 | } | ||
1687 | if ((0 == (pc->options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY)) && | ||
1688 | (GNUNET_YES != pc->all_done)) | ||
1689 | { | ||
1690 | pc->dsh = GNUNET_DATASTORE_connect (h->cfg); | ||
1691 | if (NULL == pc->dsh) | ||
1692 | goto cleanup; | ||
1693 | } | ||
1694 | if (NULL != fi_pos) | ||
1695 | { | ||
1696 | pc->fi_pos = find_file_position (pc->fi, fi_pos); | ||
1697 | GNUNET_free (fi_pos); | ||
1698 | fi_pos = NULL; | ||
1699 | if (NULL == pc->fi_pos) | ||
1700 | { | ||
1701 | /* failed to find position for resuming, outch! Will start from root! */ | ||
1702 | GNUNET_break (0); | ||
1703 | if (GNUNET_YES != pc->all_done) | ||
1704 | pc->fi_pos = pc->fi; | ||
1705 | } | ||
1706 | } | ||
1707 | GNUNET_free (fi_root); | ||
1708 | fi_root = NULL; | ||
1709 | /* generate RESUME event(s) */ | ||
1710 | GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_resume, pc); | ||
1711 | |||
1712 | /* re-start publishing (if needed)... */ | ||
1713 | if (GNUNET_YES != pc->all_done) | ||
1714 | { | ||
1715 | GNUNET_assert (NULL == pc->upload_task); | ||
1716 | pc->upload_task = | ||
1717 | GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, | ||
1718 | &GNUNET_FS_publish_main_, | ||
1719 | pc); | ||
1720 | } | ||
1721 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
1722 | { | ||
1723 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1724 | _ ("Failure while resuming publishing operation `%s': %s\n"), | ||
1725 | filename, | ||
1726 | emsg); | ||
1727 | GNUNET_free (emsg); | ||
1728 | } | ||
1729 | pc->top = GNUNET_FS_make_top (h, &GNUNET_FS_publish_signal_suspend_, pc); | ||
1730 | return GNUNET_OK; | ||
1731 | cleanup: | ||
1732 | GNUNET_free (pc->nid); | ||
1733 | GNUNET_free (pc->nuid); | ||
1734 | GNUNET_free (fi_root); | ||
1735 | GNUNET_free (fi_pos); | ||
1736 | if ((NULL != rh) && (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))) | ||
1737 | { | ||
1738 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1739 | _ ("Failed to resume publishing operation `%s': %s\n"), | ||
1740 | filename, | ||
1741 | emsg); | ||
1742 | GNUNET_free (emsg); | ||
1743 | } | ||
1744 | if (NULL != pc->fi) | ||
1745 | GNUNET_FS_file_information_destroy (pc->fi, NULL, NULL); | ||
1746 | if (0 != unlink (filename)) | ||
1747 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", filename); | ||
1748 | GNUNET_free (pc->serialization); | ||
1749 | GNUNET_free (pc); | ||
1750 | return GNUNET_OK; | ||
1751 | } | ||
1752 | |||
1753 | |||
1754 | /** | ||
1755 | * Synchronize this publishing struct with its mirror | ||
1756 | * on disk. Note that all internal FS-operations that change | ||
1757 | * publishing structs should already call "sync" internally, | ||
1758 | * so this function is likely not useful for clients. | ||
1759 | * | ||
1760 | * @param pc the struct to sync | ||
1761 | */ | ||
1762 | void | ||
1763 | GNUNET_FS_publish_sync_ (struct GNUNET_FS_PublishContext *pc) | ||
1764 | { | ||
1765 | struct GNUNET_BIO_WriteHandle *wh; | ||
1766 | int32_t have_ns; | ||
1767 | |||
1768 | if (NULL == pc->serialization) | ||
1769 | pc->serialization = | ||
1770 | make_serialization_file_name (pc->h, GNUNET_FS_SYNC_PATH_MASTER_PUBLISH); | ||
1771 | if (NULL == pc->serialization) | ||
1772 | return; | ||
1773 | if (NULL == pc->fi) | ||
1774 | return; | ||
1775 | if (NULL == pc->fi->serialization) | ||
1776 | { | ||
1777 | GNUNET_break (0); | ||
1778 | return; | ||
1779 | } | ||
1780 | wh = get_write_handle (pc->h, | ||
1781 | GNUNET_FS_SYNC_PATH_MASTER_PUBLISH, | ||
1782 | pc->serialization); | ||
1783 | if (NULL == wh) | ||
1784 | { | ||
1785 | GNUNET_break (0); | ||
1786 | goto cleanup; | ||
1787 | } | ||
1788 | have_ns = (NULL != pc->ns) ? GNUNET_YES : GNUNET_NO; | ||
1789 | struct GNUNET_BIO_WriteSpec ws[] = { | ||
1790 | GNUNET_BIO_write_spec_string ("nid", pc->nid), | ||
1791 | GNUNET_BIO_write_spec_string ("nuid", pc->nuid), | ||
1792 | GNUNET_BIO_write_spec_int32 ("options", (int32_t *) &pc->options), | ||
1793 | GNUNET_BIO_write_spec_int32 ("all done", &pc->all_done), | ||
1794 | GNUNET_BIO_write_spec_int32 ("have ns", &have_ns), | ||
1795 | GNUNET_BIO_write_spec_string ("serialization", pc->fi->serialization), | ||
1796 | GNUNET_BIO_write_spec_string ("pos serialization", (NULL == pc->fi_pos) | ||
1797 | ? NULL | ||
1798 | : pc->fi_pos->serialization), | ||
1799 | GNUNET_BIO_read_spec_end () | ||
1800 | }; | ||
1801 | if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws)) || | ||
1802 | ((NULL != pc->ns) && | ||
1803 | (GNUNET_OK != | ||
1804 | GNUNET_BIO_write (wh, | ||
1805 | "ns", | ||
1806 | pc->ns, | ||
1807 | sizeof(struct GNUNET_CRYPTO_EcdsaPrivateKey))))) | ||
1808 | { | ||
1809 | GNUNET_break (0); | ||
1810 | goto cleanup; | ||
1811 | } | ||
1812 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
1813 | { | ||
1814 | wh = NULL; | ||
1815 | GNUNET_break (0); | ||
1816 | goto cleanup; | ||
1817 | } | ||
1818 | return; | ||
1819 | cleanup: | ||
1820 | if (NULL != wh) | ||
1821 | (void) GNUNET_BIO_write_close (wh, NULL); | ||
1822 | GNUNET_FS_remove_sync_file_ (pc->h, | ||
1823 | GNUNET_FS_SYNC_PATH_MASTER_PUBLISH, | ||
1824 | pc->serialization); | ||
1825 | GNUNET_free (pc->serialization); | ||
1826 | pc->serialization = NULL; | ||
1827 | } | ||
1828 | |||
1829 | |||
1830 | /** | ||
1831 | * Synchronize this unindex struct with its mirror | ||
1832 | * on disk. Note that all internal FS-operations that change | ||
1833 | * publishing structs should already call "sync" internally, | ||
1834 | * so this function is likely not useful for clients. | ||
1835 | * | ||
1836 | * @param uc the struct to sync | ||
1837 | */ | ||
1838 | void | ||
1839 | GNUNET_FS_unindex_sync_ (struct GNUNET_FS_UnindexContext *uc) | ||
1840 | { | ||
1841 | struct GNUNET_BIO_WriteHandle *wh; | ||
1842 | char *uris; | ||
1843 | |||
1844 | if (NULL == uc->serialization) | ||
1845 | uc->serialization = | ||
1846 | make_serialization_file_name (uc->h, GNUNET_FS_SYNC_PATH_MASTER_UNINDEX); | ||
1847 | if (NULL == uc->serialization) | ||
1848 | return; | ||
1849 | wh = get_write_handle (uc->h, | ||
1850 | GNUNET_FS_SYNC_PATH_MASTER_UNINDEX, | ||
1851 | uc->serialization); | ||
1852 | if (NULL == wh) | ||
1853 | { | ||
1854 | GNUNET_break (0); | ||
1855 | goto cleanup; | ||
1856 | } | ||
1857 | if (NULL != uc->ksk_uri) | ||
1858 | uris = GNUNET_FS_uri_to_string (uc->ksk_uri); | ||
1859 | else | ||
1860 | uris = NULL; | ||
1861 | struct GNUNET_BIO_WriteSpec ws1[] = { | ||
1862 | GNUNET_BIO_write_spec_string ("filename", uc->filename), | ||
1863 | GNUNET_BIO_write_spec_int64 ("file size", (int64_t *) &uc->file_size), | ||
1864 | GNUNET_BIO_write_spec_end (), | ||
1865 | }; | ||
1866 | struct GNUNET_BIO_WriteSpec ws2[] = { | ||
1867 | GNUNET_BIO_write_spec_int32 ("state", (int32_t *) &uc->state), | ||
1868 | GNUNET_BIO_write_spec_object ("hashkey", &uc->chk, | ||
1869 | sizeof (struct ContentHashKey)), | ||
1870 | GNUNET_BIO_write_spec_string ("uris", uris), | ||
1871 | GNUNET_BIO_write_spec_int32 ("ksk offset", (int32_t *) &uc->ksk_offset), | ||
1872 | GNUNET_BIO_write_spec_end (), | ||
1873 | }; | ||
1874 | if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws1)) || | ||
1875 | (GNUNET_OK != write_start_time (wh, uc->start_time)) || | ||
1876 | (GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws2)) || | ||
1877 | ((uc->state == UNINDEX_STATE_FS_NOTIFY) && | ||
1878 | (GNUNET_OK != GNUNET_BIO_write (wh, | ||
1879 | "file id", | ||
1880 | &uc->file_id, | ||
1881 | sizeof(struct GNUNET_HashCode)))) || | ||
1882 | ((uc->state == UNINDEX_STATE_ERROR) && | ||
1883 | (GNUNET_OK != GNUNET_BIO_write_string (wh, "emsg", uc->emsg)))) | ||
1884 | { | ||
1885 | GNUNET_break (0); | ||
1886 | goto cleanup; | ||
1887 | } | ||
1888 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
1889 | { | ||
1890 | wh = NULL; | ||
1891 | GNUNET_break (0); | ||
1892 | goto cleanup; | ||
1893 | } | ||
1894 | return; | ||
1895 | cleanup: | ||
1896 | if (NULL != wh) | ||
1897 | (void) GNUNET_BIO_write_close (wh, NULL); | ||
1898 | GNUNET_FS_remove_sync_file_ (uc->h, | ||
1899 | GNUNET_FS_SYNC_PATH_MASTER_UNINDEX, | ||
1900 | uc->serialization); | ||
1901 | GNUNET_free (uc->serialization); | ||
1902 | uc->serialization = NULL; | ||
1903 | } | ||
1904 | |||
1905 | |||
1906 | /** | ||
1907 | * Serialize a download request. | ||
1908 | * | ||
1909 | * @param wh handle for writing the download request to disk | ||
1910 | * @param dr the the request to write to disk | ||
1911 | * @return #GNUNET_YES on success, #GNUNET_NO on error | ||
1912 | */ | ||
1913 | static int | ||
1914 | write_download_request (struct GNUNET_BIO_WriteHandle *wh, | ||
1915 | struct DownloadRequest *dr) | ||
1916 | { | ||
1917 | unsigned int i; | ||
1918 | struct GNUNET_BIO_WriteSpec ws[] = { | ||
1919 | GNUNET_BIO_write_spec_int32 ("state", (int32_t *) &dr->state), | ||
1920 | GNUNET_BIO_write_spec_int64 ("offset", (int64_t *) &dr->offset), | ||
1921 | GNUNET_BIO_write_spec_int32 ("num children", (int32_t *) &dr->num_children), | ||
1922 | GNUNET_BIO_write_spec_int32 ("depth", (int32_t *) &dr->depth), | ||
1923 | GNUNET_BIO_write_spec_end (), | ||
1924 | }; | ||
1925 | |||
1926 | if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws))) | ||
1927 | return GNUNET_NO; | ||
1928 | if ((BRS_CHK_SET == dr->state) && | ||
1929 | (GNUNET_OK != | ||
1930 | GNUNET_BIO_write (wh, "hashkey", | ||
1931 | &dr->chk, sizeof(struct ContentHashKey)))) | ||
1932 | return GNUNET_NO; | ||
1933 | for (i = 0; i < dr->num_children; i++) | ||
1934 | if (GNUNET_NO == write_download_request (wh, dr->children[i])) | ||
1935 | return GNUNET_NO; | ||
1936 | return GNUNET_YES; | ||
1937 | } | ||
1938 | |||
1939 | |||
1940 | /** | ||
1941 | * Read a download request tree. | ||
1942 | * | ||
1943 | * @param rh cadet to read from | ||
1944 | * @return value the download request read from disk, NULL on error | ||
1945 | */ | ||
1946 | static struct DownloadRequest * | ||
1947 | read_download_request (struct GNUNET_BIO_ReadHandle *rh) | ||
1948 | { | ||
1949 | struct DownloadRequest *dr; | ||
1950 | unsigned int i; | ||
1951 | |||
1952 | dr = GNUNET_new (struct DownloadRequest); | ||
1953 | struct GNUNET_BIO_ReadSpec rs[] = { | ||
1954 | GNUNET_BIO_read_spec_int32 ("state", (int32_t *) &dr->state), | ||
1955 | GNUNET_BIO_read_spec_int64 ("offset", (int64_t *) &dr->offset), | ||
1956 | GNUNET_BIO_read_spec_int32 ("num children", (int32_t *) &dr->num_children), | ||
1957 | GNUNET_BIO_read_spec_end (), | ||
1958 | }; | ||
1959 | if ((GNUNET_OK != GNUNET_BIO_read_spec_commit (rh, rs)) || | ||
1960 | (dr->num_children > CHK_PER_INODE) || | ||
1961 | (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "depth", | ||
1962 | (int32_t *) &dr->depth)) || | ||
1963 | ((0 == dr->depth) && (dr->num_children > 0)) || | ||
1964 | ((dr->depth > 0) && (0 == dr->num_children))) | ||
1965 | { | ||
1966 | GNUNET_break (0); | ||
1967 | dr->num_children = 0; | ||
1968 | goto cleanup; | ||
1969 | } | ||
1970 | if (dr->num_children > 0) | ||
1971 | dr->children = | ||
1972 | GNUNET_malloc (dr->num_children * sizeof(struct DownloadRequest *)); | ||
1973 | switch (dr->state) | ||
1974 | { | ||
1975 | case BRS_INIT: | ||
1976 | case BRS_RECONSTRUCT_DOWN: | ||
1977 | case BRS_RECONSTRUCT_META_UP: | ||
1978 | case BRS_RECONSTRUCT_UP: | ||
1979 | break; | ||
1980 | |||
1981 | case BRS_CHK_SET: | ||
1982 | if (GNUNET_OK != | ||
1983 | GNUNET_BIO_read (rh, "chk", &dr->chk, sizeof(struct ContentHashKey))) | ||
1984 | goto cleanup; | ||
1985 | break; | ||
1986 | |||
1987 | case BRS_DOWNLOAD_DOWN: | ||
1988 | case BRS_DOWNLOAD_UP: | ||
1989 | case BRS_ERROR: | ||
1990 | break; | ||
1991 | |||
1992 | default: | ||
1993 | GNUNET_break (0); | ||
1994 | goto cleanup; | ||
1995 | } | ||
1996 | for (i = 0; i < dr->num_children; i++) | ||
1997 | { | ||
1998 | if (NULL == (dr->children[i] = read_download_request (rh))) | ||
1999 | goto cleanup; | ||
2000 | dr->children[i]->parent = dr; | ||
2001 | } | ||
2002 | return dr; | ||
2003 | cleanup: | ||
2004 | GNUNET_FS_free_download_request_ (dr); | ||
2005 | return NULL; | ||
2006 | } | ||
2007 | |||
2008 | |||
2009 | /** | ||
2010 | * Compute the name of the sync file (or directory) for the given download | ||
2011 | * context. | ||
2012 | * | ||
2013 | * @param dc download context to compute for | ||
2014 | * @param uni unique filename to use, use "" for the directory name | ||
2015 | * @param ext extension to use, use ".dir" for our own subdirectory | ||
2016 | * @return the expanded file name, NULL for none | ||
2017 | */ | ||
2018 | static char * | ||
2019 | get_download_sync_filename (struct GNUNET_FS_DownloadContext *dc, | ||
2020 | const char *uni, | ||
2021 | const char *ext) | ||
2022 | { | ||
2023 | char *par; | ||
2024 | char *epar; | ||
2025 | |||
2026 | if (dc->parent == NULL) | ||
2027 | return get_serialization_file_name (dc->h, | ||
2028 | (dc->search != NULL) | ||
2029 | ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD | ||
2030 | : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD, | ||
2031 | uni); | ||
2032 | if (NULL == dc->parent->serialization) | ||
2033 | return NULL; | ||
2034 | par = get_download_sync_filename (dc->parent, dc->parent->serialization, ""); | ||
2035 | if (NULL == par) | ||
2036 | return NULL; | ||
2037 | GNUNET_asprintf (&epar, "%s.dir%s%s%s", par, DIR_SEPARATOR_STR, uni, ext); | ||
2038 | GNUNET_free (par); | ||
2039 | return epar; | ||
2040 | } | ||
2041 | |||
2042 | |||
2043 | /** | ||
2044 | * Synchronize this download struct with its mirror | ||
2045 | * on disk. Note that all internal FS-operations that change | ||
2046 | * publishing structs should already call "sync" internally, | ||
2047 | * so this function is likely not useful for clients. | ||
2048 | * | ||
2049 | * @param dc the struct to sync | ||
2050 | */ | ||
2051 | void | ||
2052 | GNUNET_FS_download_sync_ (struct GNUNET_FS_DownloadContext *dc) | ||
2053 | { | ||
2054 | struct GNUNET_BIO_WriteHandle *wh; | ||
2055 | char *uris; | ||
2056 | char *fn; | ||
2057 | char *dir; | ||
2058 | |||
2059 | if (0 != (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE)) | ||
2060 | return; /* we don't sync probes */ | ||
2061 | if (NULL == dc->serialization) | ||
2062 | { | ||
2063 | dir = get_download_sync_filename (dc, "", ""); | ||
2064 | if (NULL == dir) | ||
2065 | return; | ||
2066 | if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (dir)) | ||
2067 | { | ||
2068 | GNUNET_free (dir); | ||
2069 | return; | ||
2070 | } | ||
2071 | fn = GNUNET_DISK_mktemp (dir); | ||
2072 | GNUNET_free (dir); | ||
2073 | if (NULL == fn) | ||
2074 | return; | ||
2075 | dc->serialization = get_serialization_short_name (fn); | ||
2076 | } | ||
2077 | else | ||
2078 | { | ||
2079 | fn = get_download_sync_filename (dc, dc->serialization, ""); | ||
2080 | if (NULL == fn) | ||
2081 | { | ||
2082 | GNUNET_free (dc->serialization); | ||
2083 | dc->serialization = NULL; | ||
2084 | GNUNET_free (fn); | ||
2085 | return; | ||
2086 | } | ||
2087 | } | ||
2088 | wh = GNUNET_BIO_write_open_file (fn); | ||
2089 | if (NULL == wh) | ||
2090 | { | ||
2091 | GNUNET_free (dc->serialization); | ||
2092 | dc->serialization = NULL; | ||
2093 | GNUNET_free (fn); | ||
2094 | return; | ||
2095 | } | ||
2096 | GNUNET_assert ((GNUNET_YES == GNUNET_FS_uri_test_chk (dc->uri)) || | ||
2097 | (GNUNET_YES == GNUNET_FS_uri_test_loc (dc->uri))); | ||
2098 | uris = GNUNET_FS_uri_to_string (dc->uri); | ||
2099 | struct GNUNET_BIO_WriteSpec ws1[] = { | ||
2100 | GNUNET_BIO_write_spec_string ("uris", uris), | ||
2101 | GNUNET_BIO_write_spec_meta_data ("metadata", dc->meta), | ||
2102 | GNUNET_BIO_write_spec_string ("emsg", dc->emsg), | ||
2103 | GNUNET_BIO_write_spec_string ("filename", dc->filename), | ||
2104 | GNUNET_BIO_write_spec_string ("temp filename", dc->temp_filename), | ||
2105 | GNUNET_BIO_write_spec_int64 ("old file size", | ||
2106 | (int64_t *) &dc->old_file_size), | ||
2107 | GNUNET_BIO_write_spec_int64 ("offset", (int64_t *) &dc->offset), | ||
2108 | GNUNET_BIO_write_spec_int64 ("length", (int64_t *) &dc->length), | ||
2109 | GNUNET_BIO_write_spec_int64 ("completed", (int64_t *) &dc->completed), | ||
2110 | GNUNET_BIO_write_spec_end (), | ||
2111 | }; | ||
2112 | struct GNUNET_BIO_WriteSpec ws2[] = { | ||
2113 | GNUNET_BIO_write_spec_int32 ("anonymity", (int32_t *) &dc->anonymity), | ||
2114 | GNUNET_BIO_write_spec_int32 ("options", (int32_t *) &dc->options), | ||
2115 | GNUNET_BIO_write_spec_int32 ("has finished", (int32_t *) &dc->has_finished), | ||
2116 | GNUNET_BIO_write_spec_end (), | ||
2117 | }; | ||
2118 | if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws1)) || | ||
2119 | (GNUNET_OK != write_start_time (wh, dc->start_time)) || | ||
2120 | (GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws2))) | ||
2121 | { | ||
2122 | GNUNET_break (0); | ||
2123 | goto cleanup; | ||
2124 | } | ||
2125 | if (NULL == dc->emsg) | ||
2126 | { | ||
2127 | GNUNET_assert (dc->top_request != NULL); | ||
2128 | if (GNUNET_YES != write_download_request (wh, dc->top_request)) | ||
2129 | { | ||
2130 | GNUNET_break (0); | ||
2131 | goto cleanup; | ||
2132 | } | ||
2133 | } | ||
2134 | GNUNET_free (uris); | ||
2135 | uris = NULL; | ||
2136 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
2137 | { | ||
2138 | wh = NULL; | ||
2139 | GNUNET_break (0); | ||
2140 | goto cleanup; | ||
2141 | } | ||
2142 | GNUNET_free (fn); | ||
2143 | return; | ||
2144 | cleanup: | ||
2145 | if (NULL != wh) | ||
2146 | (void) GNUNET_BIO_write_close (wh, NULL); | ||
2147 | GNUNET_free (uris); | ||
2148 | if (0 != unlink (fn)) | ||
2149 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn); | ||
2150 | GNUNET_free (fn); | ||
2151 | GNUNET_free (dc->serialization); | ||
2152 | dc->serialization = NULL; | ||
2153 | } | ||
2154 | |||
2155 | |||
2156 | /** | ||
2157 | * Synchronize this search result with its mirror | ||
2158 | * on disk. Note that all internal FS-operations that change | ||
2159 | * publishing structs should already call "sync" internally, | ||
2160 | * so this function is likely not useful for clients. | ||
2161 | * | ||
2162 | * @param sr the struct to sync | ||
2163 | */ | ||
2164 | void | ||
2165 | GNUNET_FS_search_result_sync_ (struct GNUNET_FS_SearchResult *sr) | ||
2166 | { | ||
2167 | struct GNUNET_BIO_WriteHandle *wh; | ||
2168 | char *uris; | ||
2169 | |||
2170 | if (NULL == sr->sc) | ||
2171 | return; | ||
2172 | uris = NULL; | ||
2173 | if (NULL == sr->serialization) | ||
2174 | sr->serialization = | ||
2175 | make_serialization_file_name_in_dir (sr->h, | ||
2176 | (sr->sc->psearch_result == NULL) | ||
2177 | ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH | ||
2178 | : GNUNET_FS_SYNC_PATH_CHILD_SEARCH, | ||
2179 | sr->sc->serialization); | ||
2180 | if (NULL == sr->serialization) | ||
2181 | return; | ||
2182 | wh = get_write_handle_in_dir (sr->h, | ||
2183 | (sr->sc->psearch_result == NULL) | ||
2184 | ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH | ||
2185 | : GNUNET_FS_SYNC_PATH_CHILD_SEARCH, | ||
2186 | sr->sc->serialization, | ||
2187 | sr->serialization); | ||
2188 | if (NULL == wh) | ||
2189 | { | ||
2190 | GNUNET_break (0); | ||
2191 | goto cleanup; | ||
2192 | } | ||
2193 | uris = GNUNET_FS_uri_to_string (sr->uri); | ||
2194 | struct GNUNET_BIO_WriteSpec ws[] = { | ||
2195 | GNUNET_BIO_write_spec_string ("uris", uris), | ||
2196 | GNUNET_BIO_write_spec_string ("download serialization", | ||
2197 | (sr->download != NULL) | ||
2198 | ? sr->download->serialization | ||
2199 | : NULL), | ||
2200 | GNUNET_BIO_write_spec_string ("update search serialization", | ||
2201 | (sr->update_search != NULL) | ||
2202 | ? sr->update_search->serialization | ||
2203 | : NULL), | ||
2204 | GNUNET_BIO_write_spec_meta_data ("metadata", sr->meta), | ||
2205 | GNUNET_BIO_write_spec_object ("key", &sr->key, | ||
2206 | sizeof(struct GNUNET_HashCode)), | ||
2207 | GNUNET_BIO_write_spec_int32 ("mandatory missing", | ||
2208 | (int32_t *) &sr->mandatory_missing), | ||
2209 | GNUNET_BIO_write_spec_int32 ("optional support", | ||
2210 | (int32_t *) &sr->optional_support), | ||
2211 | GNUNET_BIO_write_spec_int32 ("availability success", | ||
2212 | (int32_t *) &sr->availability_success), | ||
2213 | GNUNET_BIO_write_spec_int32 ("availability trials", | ||
2214 | (int32_t *) &sr->availability_trials), | ||
2215 | GNUNET_BIO_write_spec_end (), | ||
2216 | }; | ||
2217 | if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws))) | ||
2218 | { | ||
2219 | GNUNET_break (0); | ||
2220 | goto cleanup; | ||
2221 | } | ||
2222 | if ((NULL != sr->uri) && (GNUNET_FS_URI_KSK == sr->sc->uri->type) && | ||
2223 | (GNUNET_OK != | ||
2224 | GNUNET_BIO_write (wh, | ||
2225 | "keyword bitmap", | ||
2226 | sr->keyword_bitmap, | ||
2227 | (sr->sc->uri->data.ksk.keywordCount + 7) / 8))) | ||
2228 | { | ||
2229 | GNUNET_break (0); | ||
2230 | goto cleanup; | ||
2231 | } | ||
2232 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
2233 | { | ||
2234 | wh = NULL; | ||
2235 | GNUNET_break (0); | ||
2236 | goto cleanup; | ||
2237 | } | ||
2238 | GNUNET_free (uris); | ||
2239 | return; | ||
2240 | cleanup: | ||
2241 | GNUNET_free (uris); | ||
2242 | if (NULL != wh) | ||
2243 | (void) GNUNET_BIO_write_close (wh, NULL); | ||
2244 | remove_sync_file_in_dir (sr->h, | ||
2245 | (NULL == sr->sc->psearch_result) | ||
2246 | ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH | ||
2247 | : GNUNET_FS_SYNC_PATH_CHILD_SEARCH, | ||
2248 | sr->sc->serialization, | ||
2249 | sr->serialization); | ||
2250 | GNUNET_free (sr->serialization); | ||
2251 | sr->serialization = NULL; | ||
2252 | } | ||
2253 | |||
2254 | |||
2255 | /** | ||
2256 | * Synchronize this search struct with its mirror | ||
2257 | * on disk. Note that all internal FS-operations that change | ||
2258 | * publishing structs should already call "sync" internally, | ||
2259 | * so this function is likely not useful for clients. | ||
2260 | * | ||
2261 | * @param sc the struct to sync | ||
2262 | */ | ||
2263 | void | ||
2264 | GNUNET_FS_search_sync_ (struct GNUNET_FS_SearchContext *sc) | ||
2265 | { | ||
2266 | struct GNUNET_BIO_WriteHandle *wh; | ||
2267 | char *uris; | ||
2268 | char in_pause; | ||
2269 | const char *category; | ||
2270 | |||
2271 | category = (NULL == sc->psearch_result) ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH | ||
2272 | : GNUNET_FS_SYNC_PATH_CHILD_SEARCH; | ||
2273 | if (NULL == sc->serialization) | ||
2274 | sc->serialization = make_serialization_file_name (sc->h, category); | ||
2275 | if (NULL == sc->serialization) | ||
2276 | return; | ||
2277 | uris = NULL; | ||
2278 | wh = get_write_handle (sc->h, category, sc->serialization); | ||
2279 | if (NULL == wh) | ||
2280 | { | ||
2281 | GNUNET_break (0); | ||
2282 | goto cleanup; | ||
2283 | } | ||
2284 | GNUNET_assert ((GNUNET_YES == GNUNET_FS_uri_test_ksk (sc->uri)) || | ||
2285 | (GNUNET_YES == GNUNET_FS_uri_test_sks (sc->uri))); | ||
2286 | uris = GNUNET_FS_uri_to_string (sc->uri); | ||
2287 | in_pause = (sc->task != NULL) ? 'r' : '\0'; | ||
2288 | if ((GNUNET_OK != GNUNET_BIO_write_string (wh, "uris", uris)) || | ||
2289 | (GNUNET_OK != write_start_time (wh, sc->start_time)) || | ||
2290 | (GNUNET_OK != GNUNET_BIO_write_string (wh, "emsg", sc->emsg)) || | ||
2291 | (GNUNET_OK != GNUNET_BIO_write_int32 (wh, "options", | ||
2292 | (uint32_t) sc->options)) || | ||
2293 | (GNUNET_OK != GNUNET_BIO_write (wh, "in pause", | ||
2294 | &in_pause, sizeof(in_pause))) || | ||
2295 | (GNUNET_OK != GNUNET_BIO_write_int32 (wh, "anonymity", sc->anonymity))) | ||
2296 | { | ||
2297 | GNUNET_break (0); | ||
2298 | goto cleanup; | ||
2299 | } | ||
2300 | GNUNET_free (uris); | ||
2301 | uris = NULL; | ||
2302 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
2303 | { | ||
2304 | wh = NULL; | ||
2305 | GNUNET_break (0); | ||
2306 | goto cleanup; | ||
2307 | } | ||
2308 | return; | ||
2309 | cleanup: | ||
2310 | if (NULL != wh) | ||
2311 | (void) GNUNET_BIO_write_close (wh, NULL); | ||
2312 | GNUNET_free (uris); | ||
2313 | GNUNET_FS_remove_sync_file_ (sc->h, category, sc->serialization); | ||
2314 | GNUNET_free (sc->serialization); | ||
2315 | sc->serialization = NULL; | ||
2316 | } | ||
2317 | |||
2318 | |||
2319 | /** | ||
2320 | * Function called with a filename of serialized unindexing operation | ||
2321 | * to deserialize. | ||
2322 | * | ||
2323 | * @param cls the `struct GNUNET_FS_Handle *` | ||
2324 | * @param filename complete filename (absolute path) | ||
2325 | * @return #GNUNET_OK (continue to iterate) | ||
2326 | */ | ||
2327 | static int | ||
2328 | deserialize_unindex_file (void *cls, const char *filename) | ||
2329 | { | ||
2330 | struct GNUNET_FS_Handle *h = cls; | ||
2331 | struct GNUNET_BIO_ReadHandle *rh; | ||
2332 | struct GNUNET_FS_UnindexContext *uc; | ||
2333 | struct GNUNET_FS_ProgressInfo pi; | ||
2334 | char *emsg; | ||
2335 | char *uris; | ||
2336 | uint32_t state; | ||
2337 | |||
2338 | uc = GNUNET_new (struct GNUNET_FS_UnindexContext); | ||
2339 | uc->h = h; | ||
2340 | uc->serialization = get_serialization_short_name (filename); | ||
2341 | rh = GNUNET_BIO_read_open_file (filename); | ||
2342 | if (NULL == rh) | ||
2343 | { | ||
2344 | GNUNET_break (0); | ||
2345 | goto cleanup; | ||
2346 | } | ||
2347 | uris = NULL; | ||
2348 | if ((GNUNET_OK != | ||
2349 | GNUNET_BIO_read_string (rh, "unindex-fn", &uc->filename, 10 * 1024)) || | ||
2350 | (GNUNET_OK != GNUNET_BIO_read_int64 (rh, "file size", | ||
2351 | (int64_t *) &uc->file_size)) || | ||
2352 | (GNUNET_OK != read_start_time (rh, &uc->start_time)) || | ||
2353 | (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "state", | ||
2354 | (int32_t *) &state)) || | ||
2355 | (GNUNET_OK != | ||
2356 | GNUNET_BIO_read (rh, "uri", &uc->chk, sizeof(struct ContentHashKey))) || | ||
2357 | (GNUNET_OK != | ||
2358 | GNUNET_BIO_read_string (rh, "unindex-kskuri", &uris, 10 * 1024)) || | ||
2359 | (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "ksk offset", | ||
2360 | (int32_t *) &uc->ksk_offset))) | ||
2361 | { | ||
2362 | GNUNET_free (uris); | ||
2363 | GNUNET_break (0); | ||
2364 | goto cleanup; | ||
2365 | } | ||
2366 | if (NULL != uris) | ||
2367 | { | ||
2368 | uc->ksk_uri = GNUNET_FS_uri_parse (uris, &emsg); | ||
2369 | GNUNET_free (uris); | ||
2370 | if (NULL == uc->ksk_uri) | ||
2371 | { | ||
2372 | GNUNET_break (0); | ||
2373 | GNUNET_free (emsg); | ||
2374 | goto cleanup; | ||
2375 | } | ||
2376 | } | ||
2377 | if ((uc->ksk_offset > 0) && | ||
2378 | ((NULL == uc->ksk_uri) || | ||
2379 | (uc->ksk_offset > uc->ksk_uri->data.ksk.keywordCount))) | ||
2380 | { | ||
2381 | GNUNET_break (0); | ||
2382 | goto cleanup; | ||
2383 | } | ||
2384 | uc->state = (enum UnindexState) state; | ||
2385 | switch (state) | ||
2386 | { | ||
2387 | case UNINDEX_STATE_HASHING: | ||
2388 | break; | ||
2389 | |||
2390 | case UNINDEX_STATE_FS_NOTIFY: | ||
2391 | if (GNUNET_OK != GNUNET_BIO_read (rh, | ||
2392 | "unindex-hash", | ||
2393 | &uc->file_id, | ||
2394 | sizeof(struct GNUNET_HashCode))) | ||
2395 | { | ||
2396 | GNUNET_break (0); | ||
2397 | goto cleanup; | ||
2398 | } | ||
2399 | break; | ||
2400 | |||
2401 | case UNINDEX_STATE_DS_REMOVE: | ||
2402 | case UNINDEX_STATE_EXTRACT_KEYWORDS: | ||
2403 | case UNINDEX_STATE_DS_REMOVE_KBLOCKS: | ||
2404 | break; | ||
2405 | |||
2406 | case UNINDEX_STATE_COMPLETE: | ||
2407 | break; | ||
2408 | |||
2409 | case UNINDEX_STATE_ERROR: | ||
2410 | if (GNUNET_OK != | ||
2411 | GNUNET_BIO_read_string (rh, "unindex-emsg", &uc->emsg, 10 * 1024)) | ||
2412 | { | ||
2413 | GNUNET_break (0); | ||
2414 | goto cleanup; | ||
2415 | } | ||
2416 | break; | ||
2417 | |||
2418 | default: | ||
2419 | GNUNET_break (0); | ||
2420 | goto cleanup; | ||
2421 | } | ||
2422 | uc->top = GNUNET_FS_make_top (h, &GNUNET_FS_unindex_signal_suspend_, uc); | ||
2423 | pi.status = GNUNET_FS_STATUS_UNINDEX_RESUME; | ||
2424 | pi.value.unindex.specifics.resume.message = uc->emsg; | ||
2425 | GNUNET_FS_unindex_make_status_ (&pi, | ||
2426 | uc, | ||
2427 | (uc->state == UNINDEX_STATE_COMPLETE) | ||
2428 | ? uc->file_size | ||
2429 | : 0); | ||
2430 | switch (uc->state) | ||
2431 | { | ||
2432 | case UNINDEX_STATE_HASHING: | ||
2433 | uc->fhc = GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE, | ||
2434 | uc->filename, | ||
2435 | HASHING_BLOCKSIZE, | ||
2436 | &GNUNET_FS_unindex_process_hash_, | ||
2437 | uc); | ||
2438 | break; | ||
2439 | |||
2440 | case UNINDEX_STATE_FS_NOTIFY: | ||
2441 | uc->state = UNINDEX_STATE_HASHING; | ||
2442 | GNUNET_FS_unindex_process_hash_ (uc, &uc->file_id); | ||
2443 | break; | ||
2444 | |||
2445 | case UNINDEX_STATE_DS_REMOVE: | ||
2446 | GNUNET_FS_unindex_do_remove_ (uc); | ||
2447 | break; | ||
2448 | |||
2449 | case UNINDEX_STATE_EXTRACT_KEYWORDS: | ||
2450 | GNUNET_FS_unindex_do_extract_keywords_ (uc); | ||
2451 | break; | ||
2452 | |||
2453 | case UNINDEX_STATE_DS_REMOVE_KBLOCKS: | ||
2454 | GNUNET_FS_unindex_do_remove_kblocks_ (uc); | ||
2455 | break; | ||
2456 | |||
2457 | case UNINDEX_STATE_COMPLETE: | ||
2458 | case UNINDEX_STATE_ERROR: | ||
2459 | /* no need to resume any operation, we were done */ | ||
2460 | break; | ||
2461 | |||
2462 | default: | ||
2463 | break; | ||
2464 | } | ||
2465 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
2466 | { | ||
2467 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2468 | _ ("Failure while resuming unindexing operation `%s': %s\n"), | ||
2469 | filename, | ||
2470 | emsg); | ||
2471 | GNUNET_free (emsg); | ||
2472 | } | ||
2473 | return GNUNET_OK; | ||
2474 | cleanup: | ||
2475 | GNUNET_free (uc->filename); | ||
2476 | if ((NULL != rh) && (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))) | ||
2477 | { | ||
2478 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2479 | _ ("Failed to resume unindexing operation `%s': %s\n"), | ||
2480 | filename, | ||
2481 | emsg); | ||
2482 | GNUNET_free (emsg); | ||
2483 | } | ||
2484 | if (NULL != uc->serialization) | ||
2485 | GNUNET_FS_remove_sync_file_ (h, | ||
2486 | GNUNET_FS_SYNC_PATH_MASTER_UNINDEX, | ||
2487 | uc->serialization); | ||
2488 | GNUNET_free (uc->serialization); | ||
2489 | GNUNET_free (uc); | ||
2490 | return GNUNET_OK; | ||
2491 | } | ||
2492 | |||
2493 | |||
2494 | /** | ||
2495 | * Deserialize a download. | ||
2496 | * | ||
2497 | * @param h overall context | ||
2498 | * @param rh file to deserialize from | ||
2499 | * @param parent parent download | ||
2500 | * @param search associated search | ||
2501 | * @param serialization name under which the search was serialized | ||
2502 | */ | ||
2503 | static void | ||
2504 | deserialize_download (struct GNUNET_FS_Handle *h, | ||
2505 | struct GNUNET_BIO_ReadHandle *rh, | ||
2506 | struct GNUNET_FS_DownloadContext *parent, | ||
2507 | struct GNUNET_FS_SearchResult *search, | ||
2508 | const char *serialization); | ||
2509 | |||
2510 | |||
2511 | /** | ||
2512 | * Deserialize a search. | ||
2513 | * | ||
2514 | * @param h overall context | ||
2515 | * @param rh file to deserialize from | ||
2516 | * @param psearch_result parent search result | ||
2517 | * @param serialization name under which the search was serialized | ||
2518 | */ | ||
2519 | static struct GNUNET_FS_SearchContext * | ||
2520 | deserialize_search (struct GNUNET_FS_Handle *h, | ||
2521 | struct GNUNET_BIO_ReadHandle *rh, | ||
2522 | struct GNUNET_FS_SearchResult *psearch_result, | ||
2523 | const char *serialization); | ||
2524 | |||
2525 | |||
2526 | /** | ||
2527 | * Function called with a filename of serialized search result | ||
2528 | * to deserialize. | ||
2529 | * | ||
2530 | * @param cls the `struct GNUNET_FS_SearchContext *` | ||
2531 | * @param filename complete filename (absolute path) | ||
2532 | * @return #GNUNET_OK (continue to iterate) | ||
2533 | */ | ||
2534 | static int | ||
2535 | deserialize_search_result (void *cls, const char *filename) | ||
2536 | { | ||
2537 | struct GNUNET_FS_SearchContext *sc = cls; | ||
2538 | char *serialized; | ||
2539 | char *uris; | ||
2540 | char *emsg; | ||
2541 | char *download; | ||
2542 | char *update_srch; | ||
2543 | struct GNUNET_BIO_ReadHandle *rh; | ||
2544 | struct GNUNET_BIO_ReadHandle *drh; | ||
2545 | struct GNUNET_FS_SearchResult *sr; | ||
2546 | |||
2547 | serialized = get_serialization_short_name (filename); | ||
2548 | rh = GNUNET_BIO_read_open_file (filename); | ||
2549 | if (NULL == rh) | ||
2550 | { | ||
2551 | if (NULL != serialized) | ||
2552 | { | ||
2553 | remove_sync_file_in_dir (sc->h, | ||
2554 | (NULL == sc->psearch_result) | ||
2555 | ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH | ||
2556 | : GNUNET_FS_SYNC_PATH_CHILD_SEARCH, | ||
2557 | sc->serialization, | ||
2558 | serialized); | ||
2559 | GNUNET_free (serialized); | ||
2560 | } | ||
2561 | return GNUNET_OK; | ||
2562 | } | ||
2563 | emsg = NULL; | ||
2564 | uris = NULL; | ||
2565 | download = NULL; | ||
2566 | update_srch = NULL; | ||
2567 | sr = GNUNET_new (struct GNUNET_FS_SearchResult); | ||
2568 | sr->h = sc->h; | ||
2569 | sr->sc = sc; | ||
2570 | sr->serialization = serialized; | ||
2571 | if ((GNUNET_OK != | ||
2572 | GNUNET_BIO_read_string (rh, "result-uri", &uris, 10 * 1024)) || | ||
2573 | (NULL == (sr->uri = GNUNET_FS_uri_parse (uris, &emsg))) || | ||
2574 | (GNUNET_OK != | ||
2575 | GNUNET_BIO_read_string (rh, "download-lnk", &download, 16)) || | ||
2576 | (GNUNET_OK != | ||
2577 | GNUNET_BIO_read_string (rh, "search-lnk", &update_srch, 16)) || | ||
2578 | (GNUNET_OK != GNUNET_BIO_read_meta_data (rh, "result-meta", &sr->meta)) || | ||
2579 | (GNUNET_OK != GNUNET_BIO_read (rh, | ||
2580 | "result-key", | ||
2581 | &sr->key, | ||
2582 | sizeof(struct GNUNET_HashCode))) || | ||
2583 | (GNUNET_OK != GNUNET_BIO_read_int32 ( | ||
2584 | rh, | ||
2585 | "mandatory missing", | ||
2586 | (int32_t *) &sr->mandatory_missing)) || | ||
2587 | (GNUNET_OK != GNUNET_BIO_read_int32 ( | ||
2588 | rh, | ||
2589 | "optional support", | ||
2590 | (int32_t *) &sr->optional_support)) || | ||
2591 | (GNUNET_OK != GNUNET_BIO_read_int32 ( | ||
2592 | rh, | ||
2593 | "availability success", | ||
2594 | (int32_t *) &sr->availability_success)) || | ||
2595 | (GNUNET_OK != GNUNET_BIO_read_int32 ( | ||
2596 | rh, | ||
2597 | "availability trials", | ||
2598 | (int32_t *) &sr->availability_trials))) | ||
2599 | { | ||
2600 | GNUNET_break (0); | ||
2601 | goto cleanup; | ||
2602 | } | ||
2603 | if (GNUNET_FS_URI_KSK == sr->sc->uri->type) | ||
2604 | { | ||
2605 | sr->keyword_bitmap = GNUNET_malloc ( | ||
2606 | (sr->sc->uri->data.ksk.keywordCount + 7) / 8); /* round up, count bits */ | ||
2607 | if (GNUNET_OK != | ||
2608 | GNUNET_BIO_read (rh, | ||
2609 | "keyword-bitmap", | ||
2610 | sr->keyword_bitmap, | ||
2611 | (sr->sc->uri->data.ksk.keywordCount + 7) / 8)) | ||
2612 | { | ||
2613 | GNUNET_break (0); | ||
2614 | goto cleanup; | ||
2615 | } | ||
2616 | } | ||
2617 | GNUNET_free (uris); | ||
2618 | if (NULL != download) | ||
2619 | { | ||
2620 | drh = get_read_handle (sc->h, GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD, download); | ||
2621 | if (NULL != drh) | ||
2622 | { | ||
2623 | deserialize_download (sc->h, drh, NULL, sr, download); | ||
2624 | if (GNUNET_OK != GNUNET_BIO_read_close (drh, &emsg)) | ||
2625 | { | ||
2626 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2627 | _ ("Failed to resume sub-download `%s': %s\n"), | ||
2628 | download, | ||
2629 | emsg); | ||
2630 | GNUNET_free (emsg); | ||
2631 | } | ||
2632 | } | ||
2633 | GNUNET_free (download); | ||
2634 | } | ||
2635 | if (NULL != update_srch) | ||
2636 | { | ||
2637 | drh = | ||
2638 | get_read_handle (sc->h, GNUNET_FS_SYNC_PATH_CHILD_SEARCH, update_srch); | ||
2639 | if (NULL != drh) | ||
2640 | { | ||
2641 | deserialize_search (sc->h, drh, sr, update_srch); | ||
2642 | if (GNUNET_OK != GNUNET_BIO_read_close (drh, &emsg)) | ||
2643 | { | ||
2644 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2645 | _ ("Failed to resume sub-search `%s': %s\n"), | ||
2646 | update_srch, | ||
2647 | emsg); | ||
2648 | GNUNET_free (emsg); | ||
2649 | } | ||
2650 | } | ||
2651 | GNUNET_free (update_srch); | ||
2652 | } | ||
2653 | GNUNET_break (GNUNET_YES == GNUNET_CONTAINER_multihashmap_put ( | ||
2654 | sc->master_result_map, | ||
2655 | &sr->key, | ||
2656 | sr, | ||
2657 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); | ||
2658 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
2659 | { | ||
2660 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2661 | _ ("Failure while resuming search operation `%s': %s\n"), | ||
2662 | filename, | ||
2663 | emsg); | ||
2664 | GNUNET_free (emsg); | ||
2665 | } | ||
2666 | return GNUNET_OK; | ||
2667 | cleanup: | ||
2668 | GNUNET_free (download); | ||
2669 | GNUNET_free (emsg); | ||
2670 | GNUNET_free (uris); | ||
2671 | GNUNET_free (update_srch); | ||
2672 | if (NULL != sr->uri) | ||
2673 | GNUNET_FS_uri_destroy (sr->uri); | ||
2674 | if (NULL != sr->meta) | ||
2675 | GNUNET_CONTAINER_meta_data_destroy (sr->meta); | ||
2676 | GNUNET_free (sr->serialization); | ||
2677 | GNUNET_free (sr); | ||
2678 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
2679 | { | ||
2680 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2681 | _ ("Failure while resuming search operation `%s': %s\n"), | ||
2682 | filename, | ||
2683 | emsg); | ||
2684 | GNUNET_free (emsg); | ||
2685 | } | ||
2686 | return GNUNET_OK; | ||
2687 | } | ||
2688 | |||
2689 | |||
2690 | /** | ||
2691 | * Send the 'resume' signal to the callback; also actually | ||
2692 | * resume the download (put it in the queue). Does this | ||
2693 | * recursively for the top-level download and all child | ||
2694 | * downloads. | ||
2695 | * | ||
2696 | * @param dc download to resume | ||
2697 | */ | ||
2698 | static void | ||
2699 | signal_download_resume (struct GNUNET_FS_DownloadContext *dc) | ||
2700 | { | ||
2701 | struct GNUNET_FS_DownloadContext *dcc; | ||
2702 | struct GNUNET_FS_ProgressInfo pi; | ||
2703 | |||
2704 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_RESUME; | ||
2705 | pi.value.download.specifics.resume.meta = dc->meta; | ||
2706 | pi.value.download.specifics.resume.message = dc->emsg; | ||
2707 | GNUNET_FS_download_make_status_ (&pi, dc); | ||
2708 | dcc = dc->child_head; | ||
2709 | while (NULL != dcc) | ||
2710 | { | ||
2711 | signal_download_resume (dcc); | ||
2712 | dcc = dcc->next; | ||
2713 | } | ||
2714 | } | ||
2715 | |||
2716 | |||
2717 | /** | ||
2718 | * Signal resuming of a search to our clients (for the | ||
2719 | * top level search and all sub-searches). | ||
2720 | * | ||
2721 | * @param sc search being resumed | ||
2722 | */ | ||
2723 | static void | ||
2724 | signal_search_resume (struct GNUNET_FS_SearchContext *sc); | ||
2725 | |||
2726 | |||
2727 | /** | ||
2728 | * Iterator over search results signaling resume to the client for | ||
2729 | * each result. | ||
2730 | * | ||
2731 | * @param cls closure, the `struct GNUNET_FS_SearchContext *` | ||
2732 | * @param key current key code | ||
2733 | * @param value value in the hash map, the `struct GNUNET_FS_SearchResult *` | ||
2734 | * @return #GNUNET_YES (we should continue to iterate) | ||
2735 | */ | ||
2736 | static int | ||
2737 | signal_result_resume (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
2738 | { | ||
2739 | struct GNUNET_FS_SearchContext *sc = cls; | ||
2740 | struct GNUNET_FS_ProgressInfo pi; | ||
2741 | struct GNUNET_FS_SearchResult *sr = value; | ||
2742 | |||
2743 | if (0 == sr->mandatory_missing) | ||
2744 | { | ||
2745 | pi.status = GNUNET_FS_STATUS_SEARCH_RESUME_RESULT; | ||
2746 | pi.value.search.specifics.resume_result.meta = sr->meta; | ||
2747 | pi.value.search.specifics.resume_result.uri = sr->uri; | ||
2748 | pi.value.search.specifics.resume_result.result = sr; | ||
2749 | pi.value.search.specifics.resume_result.availability_rank = | ||
2750 | 2 * sr->availability_success - sr->availability_trials; | ||
2751 | pi.value.search.specifics.resume_result.availability_certainty = | ||
2752 | sr->availability_trials; | ||
2753 | pi.value.search.specifics.resume_result.applicability_rank = | ||
2754 | sr->optional_support; | ||
2755 | sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc); | ||
2756 | } | ||
2757 | if (NULL != sr->download) | ||
2758 | { | ||
2759 | signal_download_resume (sr->download); | ||
2760 | } | ||
2761 | else | ||
2762 | { | ||
2763 | GNUNET_FS_search_start_probe_ (sr); | ||
2764 | } | ||
2765 | if (NULL != sr->update_search) | ||
2766 | signal_search_resume (sr->update_search); | ||
2767 | return GNUNET_YES; | ||
2768 | } | ||
2769 | |||
2770 | |||
2771 | /** | ||
2772 | * Free memory allocated by the search context and its children | ||
2773 | * | ||
2774 | * @param sc search context to free | ||
2775 | */ | ||
2776 | static void | ||
2777 | free_search_context (struct GNUNET_FS_SearchContext *sc); | ||
2778 | |||
2779 | |||
2780 | /** | ||
2781 | * Iterator over search results freeing each. | ||
2782 | * | ||
2783 | * @param cls closure, the `struct GNUNET_FS_SearchContext *` | ||
2784 | * @param key current key code | ||
2785 | * @param value value in the hash map, the `struct GNUNET_FS_SearchResult *` | ||
2786 | * @return #GNUNET_YES (we should continue to iterate) | ||
2787 | */ | ||
2788 | static int | ||
2789 | free_result (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
2790 | { | ||
2791 | struct GNUNET_FS_SearchResult *sr = value; | ||
2792 | |||
2793 | if (NULL != sr->update_search) | ||
2794 | { | ||
2795 | free_search_context (sr->update_search); | ||
2796 | GNUNET_assert (NULL == sr->update_search); | ||
2797 | } | ||
2798 | GNUNET_CONTAINER_meta_data_destroy (sr->meta); | ||
2799 | GNUNET_FS_uri_destroy (sr->uri); | ||
2800 | GNUNET_free (sr); | ||
2801 | return GNUNET_YES; | ||
2802 | } | ||
2803 | |||
2804 | |||
2805 | /** | ||
2806 | * Free memory allocated by the search context and its children | ||
2807 | * | ||
2808 | * @param sc search context to free | ||
2809 | */ | ||
2810 | static void | ||
2811 | free_search_context (struct GNUNET_FS_SearchContext *sc) | ||
2812 | { | ||
2813 | if (NULL != sc->serialization) | ||
2814 | { | ||
2815 | GNUNET_FS_remove_sync_file_ (sc->h, | ||
2816 | (sc->psearch_result == NULL) | ||
2817 | ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH | ||
2818 | : GNUNET_FS_SYNC_PATH_CHILD_SEARCH, | ||
2819 | sc->serialization); | ||
2820 | GNUNET_FS_remove_sync_dir_ (sc->h, | ||
2821 | (sc->psearch_result == NULL) | ||
2822 | ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH | ||
2823 | : GNUNET_FS_SYNC_PATH_CHILD_SEARCH, | ||
2824 | sc->serialization); | ||
2825 | } | ||
2826 | GNUNET_free (sc->serialization); | ||
2827 | GNUNET_free (sc->emsg); | ||
2828 | if (NULL != sc->uri) | ||
2829 | GNUNET_FS_uri_destroy (sc->uri); | ||
2830 | if (NULL != sc->master_result_map) | ||
2831 | { | ||
2832 | GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map, | ||
2833 | &free_result, | ||
2834 | sc); | ||
2835 | GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map); | ||
2836 | } | ||
2837 | GNUNET_free (sc); | ||
2838 | } | ||
2839 | |||
2840 | |||
2841 | /** | ||
2842 | * Function called with a filename of serialized sub-download | ||
2843 | * to deserialize. | ||
2844 | * | ||
2845 | * @param cls the `struct GNUNET_FS_DownloadContext *` (parent) | ||
2846 | * @param filename complete filename (absolute path) | ||
2847 | * @return #GNUNET_OK (continue to iterate) | ||
2848 | */ | ||
2849 | static int | ||
2850 | deserialize_subdownload (void *cls, const char *filename) | ||
2851 | { | ||
2852 | struct GNUNET_FS_DownloadContext *parent = cls; | ||
2853 | char *serialized; | ||
2854 | char *emsg; | ||
2855 | struct GNUNET_BIO_ReadHandle *rh; | ||
2856 | |||
2857 | serialized = get_serialization_short_name (filename); | ||
2858 | rh = GNUNET_BIO_read_open_file (filename); | ||
2859 | if (NULL == rh) | ||
2860 | { | ||
2861 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2862 | _ ( | ||
2863 | "Failed to resume sub-download `%s': could not open file `%s'\n"), | ||
2864 | serialized, | ||
2865 | filename); | ||
2866 | GNUNET_free (serialized); | ||
2867 | return GNUNET_OK; | ||
2868 | } | ||
2869 | deserialize_download (parent->h, rh, parent, NULL, serialized); | ||
2870 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
2871 | { | ||
2872 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2873 | _ ("Failed to resume sub-download `%s': %s\n"), | ||
2874 | serialized, | ||
2875 | emsg); | ||
2876 | GNUNET_free (emsg); | ||
2877 | } | ||
2878 | GNUNET_free (serialized); | ||
2879 | return GNUNET_OK; | ||
2880 | } | ||
2881 | |||
2882 | |||
2883 | /** | ||
2884 | * Free this download context and all of its descendants. | ||
2885 | * (only works during deserialization since not all possible | ||
2886 | * state it taken care of). | ||
2887 | * | ||
2888 | * @param dc context to free | ||
2889 | */ | ||
2890 | static void | ||
2891 | free_download_context (struct GNUNET_FS_DownloadContext *dc) | ||
2892 | { | ||
2893 | struct GNUNET_FS_DownloadContext *dcc; | ||
2894 | |||
2895 | if (NULL != dc->meta) | ||
2896 | GNUNET_CONTAINER_meta_data_destroy (dc->meta); | ||
2897 | if (NULL != dc->uri) | ||
2898 | GNUNET_FS_uri_destroy (dc->uri); | ||
2899 | GNUNET_free (dc->temp_filename); | ||
2900 | GNUNET_free (dc->emsg); | ||
2901 | GNUNET_free (dc->filename); | ||
2902 | GNUNET_free (dc->serialization); | ||
2903 | while (NULL != (dcc = dc->child_head)) | ||
2904 | { | ||
2905 | GNUNET_CONTAINER_DLL_remove (dc->child_head, dc->child_tail, dcc); | ||
2906 | free_download_context (dcc); | ||
2907 | } | ||
2908 | GNUNET_FS_free_download_request_ (dc->top_request); | ||
2909 | if (NULL != dc->active) | ||
2910 | GNUNET_CONTAINER_multihashmap_destroy (dc->active); | ||
2911 | GNUNET_free (dc); | ||
2912 | } | ||
2913 | |||
2914 | |||
2915 | /** | ||
2916 | * Deserialize a download. | ||
2917 | * | ||
2918 | * @param h overall context | ||
2919 | * @param rh file to deserialize from | ||
2920 | * @param parent parent download | ||
2921 | * @param search associated search | ||
2922 | * @param serialization name under which the search was serialized | ||
2923 | */ | ||
2924 | static void | ||
2925 | deserialize_download (struct GNUNET_FS_Handle *h, | ||
2926 | struct GNUNET_BIO_ReadHandle *rh, | ||
2927 | struct GNUNET_FS_DownloadContext *parent, | ||
2928 | struct GNUNET_FS_SearchResult *search, | ||
2929 | const char *serialization) | ||
2930 | { | ||
2931 | struct GNUNET_FS_DownloadContext *dc; | ||
2932 | char *emsg; | ||
2933 | char *uris; | ||
2934 | char *dn; | ||
2935 | uint32_t options; | ||
2936 | uint32_t status; | ||
2937 | |||
2938 | uris = NULL; | ||
2939 | emsg = NULL; | ||
2940 | dc = GNUNET_new (struct GNUNET_FS_DownloadContext); | ||
2941 | dc->parent = parent; | ||
2942 | dc->h = h; | ||
2943 | dc->serialization = GNUNET_strdup (serialization); | ||
2944 | struct GNUNET_BIO_ReadSpec rs[] = { | ||
2945 | GNUNET_BIO_read_spec_meta_data ("download-meta", &dc->meta), | ||
2946 | GNUNET_BIO_read_spec_string ("download-emsg", &dc->emsg, 10 * 1024), | ||
2947 | GNUNET_BIO_read_spec_string ("download-fn", &dc->filename, 10 * 1024), | ||
2948 | GNUNET_BIO_read_spec_string ("download-tfn", | ||
2949 | &dc->temp_filename, 10 * 1024), | ||
2950 | GNUNET_BIO_read_spec_int64 ("old file size", | ||
2951 | (int64_t *) &dc->old_file_size), | ||
2952 | GNUNET_BIO_read_spec_int64 ("offset", | ||
2953 | (int64_t *) &dc->offset), | ||
2954 | GNUNET_BIO_read_spec_int64 ("length", | ||
2955 | (int64_t *) &dc->length), | ||
2956 | GNUNET_BIO_read_spec_int64 ("completed", | ||
2957 | (int64_t *) &dc->completed), | ||
2958 | GNUNET_BIO_read_spec_end (), | ||
2959 | }; | ||
2960 | if ((GNUNET_OK != | ||
2961 | GNUNET_BIO_read_string (rh, "download-uri", &uris, 10 * 1024)) || | ||
2962 | (NULL == (dc->uri = GNUNET_FS_uri_parse (uris, &emsg))) || | ||
2963 | ((GNUNET_YES != GNUNET_FS_uri_test_chk (dc->uri)) && | ||
2964 | (GNUNET_YES != GNUNET_FS_uri_test_loc (dc->uri))) || | ||
2965 | (GNUNET_OK != GNUNET_BIO_read_spec_commit (rh, rs)) || | ||
2966 | (GNUNET_OK != read_start_time (rh, &dc->start_time)) || | ||
2967 | (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "anonymity", | ||
2968 | (int32_t *) &dc->anonymity)) || | ||
2969 | (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "options", | ||
2970 | (int32_t *) &options)) || | ||
2971 | (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "status", | ||
2972 | (int32_t *) &status))) | ||
2973 | { | ||
2974 | GNUNET_break (0); | ||
2975 | goto cleanup; | ||
2976 | } | ||
2977 | dc->options = (enum GNUNET_FS_DownloadOptions) options; | ||
2978 | dc->active = | ||
2979 | GNUNET_CONTAINER_multihashmap_create (1 + 2 * (dc->length / DBLOCK_SIZE), | ||
2980 | GNUNET_NO); | ||
2981 | dc->has_finished = (int) status; | ||
2982 | dc->treedepth = | ||
2983 | GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size (dc->uri)); | ||
2984 | if (GNUNET_FS_uri_test_loc (dc->uri)) | ||
2985 | GNUNET_assert (GNUNET_OK == | ||
2986 | GNUNET_FS_uri_loc_get_peer_identity (dc->uri, &dc->target)); | ||
2987 | if (NULL == dc->emsg) | ||
2988 | { | ||
2989 | dc->top_request = read_download_request (rh); | ||
2990 | if (NULL == dc->top_request) | ||
2991 | { | ||
2992 | GNUNET_break (0); | ||
2993 | goto cleanup; | ||
2994 | } | ||
2995 | } | ||
2996 | dn = get_download_sync_filename (dc, dc->serialization, ".dir"); | ||
2997 | if (NULL != dn) | ||
2998 | { | ||
2999 | if (GNUNET_YES == GNUNET_DISK_directory_test (dn, GNUNET_YES)) | ||
3000 | GNUNET_DISK_directory_scan (dn, &deserialize_subdownload, dc); | ||
3001 | GNUNET_free (dn); | ||
3002 | } | ||
3003 | if (NULL != parent) | ||
3004 | { | ||
3005 | GNUNET_CONTAINER_DLL_insert (parent->child_head, parent->child_tail, dc); | ||
3006 | } | ||
3007 | if (NULL != search) | ||
3008 | { | ||
3009 | dc->search = search; | ||
3010 | search->download = dc; | ||
3011 | } | ||
3012 | if ((NULL == parent) && (NULL == search)) | ||
3013 | { | ||
3014 | dc->top = | ||
3015 | GNUNET_FS_make_top (dc->h, &GNUNET_FS_download_signal_suspend_, dc); | ||
3016 | signal_download_resume (dc); | ||
3017 | } | ||
3018 | GNUNET_free (uris); | ||
3019 | GNUNET_assert (NULL == dc->job_queue); | ||
3020 | dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc); | ||
3021 | return; | ||
3022 | cleanup: | ||
3023 | GNUNET_free (uris); | ||
3024 | GNUNET_free (emsg); | ||
3025 | free_download_context (dc); | ||
3026 | } | ||
3027 | |||
3028 | |||
3029 | /** | ||
3030 | * Signal resuming of a search to our clients (for the | ||
3031 | * top level search and all sub-searches). | ||
3032 | * | ||
3033 | * @param sc search being resumed | ||
3034 | */ | ||
3035 | static void | ||
3036 | signal_search_resume (struct GNUNET_FS_SearchContext *sc) | ||
3037 | { | ||
3038 | struct GNUNET_FS_ProgressInfo pi; | ||
3039 | |||
3040 | pi.status = GNUNET_FS_STATUS_SEARCH_RESUME; | ||
3041 | pi.value.search.specifics.resume.message = sc->emsg; | ||
3042 | pi.value.search.specifics.resume.is_paused = | ||
3043 | (NULL == sc->mq) ? GNUNET_YES : GNUNET_NO; | ||
3044 | sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc); | ||
3045 | GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map, | ||
3046 | &signal_result_resume, | ||
3047 | sc); | ||
3048 | } | ||
3049 | |||
3050 | |||
3051 | /** | ||
3052 | * Deserialize a search. | ||
3053 | * | ||
3054 | * @param h overall context | ||
3055 | * @param rh file to deserialize from | ||
3056 | * @param psearch_result parent search result | ||
3057 | * @param serialization name under which the search was serialized | ||
3058 | */ | ||
3059 | static struct GNUNET_FS_SearchContext * | ||
3060 | deserialize_search (struct GNUNET_FS_Handle *h, | ||
3061 | struct GNUNET_BIO_ReadHandle *rh, | ||
3062 | struct GNUNET_FS_SearchResult *psearch_result, | ||
3063 | const char *serialization) | ||
3064 | { | ||
3065 | struct GNUNET_FS_SearchContext *sc; | ||
3066 | char *emsg; | ||
3067 | char *uris; | ||
3068 | char *dn; | ||
3069 | uint32_t options; | ||
3070 | char in_pause; | ||
3071 | |||
3072 | if ((NULL != psearch_result) && (NULL != psearch_result->update_search)) | ||
3073 | { | ||
3074 | GNUNET_break (0); | ||
3075 | return NULL; | ||
3076 | } | ||
3077 | uris = NULL; | ||
3078 | emsg = NULL; | ||
3079 | sc = GNUNET_new (struct GNUNET_FS_SearchContext); | ||
3080 | if (NULL != psearch_result) | ||
3081 | { | ||
3082 | sc->psearch_result = psearch_result; | ||
3083 | psearch_result->update_search = sc; | ||
3084 | } | ||
3085 | sc->h = h; | ||
3086 | sc->serialization = GNUNET_strdup (serialization); | ||
3087 | if ((GNUNET_OK != | ||
3088 | GNUNET_BIO_read_string (rh, "search-uri", &uris, 10 * 1024)) || | ||
3089 | (NULL == (sc->uri = GNUNET_FS_uri_parse (uris, &emsg))) || | ||
3090 | ((GNUNET_YES != GNUNET_FS_uri_test_ksk (sc->uri)) && | ||
3091 | (GNUNET_YES != GNUNET_FS_uri_test_sks (sc->uri))) || | ||
3092 | (GNUNET_OK != read_start_time (rh, &sc->start_time)) || | ||
3093 | (GNUNET_OK != | ||
3094 | GNUNET_BIO_read_string (rh, "search-emsg", &sc->emsg, 10 * 1024)) || | ||
3095 | (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "options", | ||
3096 | (int32_t *) &options)) || | ||
3097 | (GNUNET_OK != | ||
3098 | GNUNET_BIO_read (rh, "search-pause", &in_pause, sizeof(in_pause))) || | ||
3099 | (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "anonymity", | ||
3100 | (int32_t *) &sc->anonymity))) | ||
3101 | { | ||
3102 | GNUNET_break (0); | ||
3103 | goto cleanup; | ||
3104 | } | ||
3105 | sc->options = (enum GNUNET_FS_SearchOptions) options; | ||
3106 | sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO); | ||
3107 | dn = get_serialization_file_name_in_dir (h, | ||
3108 | (NULL == sc->psearch_result) | ||
3109 | ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH | ||
3110 | : GNUNET_FS_SYNC_PATH_CHILD_SEARCH, | ||
3111 | sc->serialization, | ||
3112 | ""); | ||
3113 | if (NULL != dn) | ||
3114 | { | ||
3115 | if (GNUNET_YES == GNUNET_DISK_directory_test (dn, GNUNET_YES)) | ||
3116 | GNUNET_DISK_directory_scan (dn, &deserialize_search_result, sc); | ||
3117 | GNUNET_free (dn); | ||
3118 | } | ||
3119 | if (('\0' == in_pause) && | ||
3120 | (GNUNET_OK != GNUNET_FS_search_start_searching_ (sc))) | ||
3121 | { | ||
3122 | GNUNET_log ( | ||
3123 | GNUNET_ERROR_TYPE_WARNING, | ||
3124 | _ ("Could not resume running search, will resume as paused search\n")); | ||
3125 | } | ||
3126 | signal_search_resume (sc); | ||
3127 | GNUNET_free (uris); | ||
3128 | return sc; | ||
3129 | cleanup: | ||
3130 | GNUNET_free (emsg); | ||
3131 | free_search_context (sc); | ||
3132 | GNUNET_free (uris); | ||
3133 | return NULL; | ||
3134 | } | ||
3135 | |||
3136 | |||
3137 | /** | ||
3138 | * Function called with a filename of serialized search operation | ||
3139 | * to deserialize. | ||
3140 | * | ||
3141 | * @param cls the `struct GNUNET_FS_Handle *` | ||
3142 | * @param filename complete filename (absolute path) | ||
3143 | * @return #GNUNET_OK (continue to iterate) | ||
3144 | */ | ||
3145 | static int | ||
3146 | deserialize_search_file (void *cls, const char *filename) | ||
3147 | { | ||
3148 | struct GNUNET_FS_Handle *h = cls; | ||
3149 | char *set; | ||
3150 | char *emsg; | ||
3151 | struct GNUNET_BIO_ReadHandle *rh; | ||
3152 | struct GNUNET_FS_SearchContext *sc; | ||
3153 | struct stat buf; | ||
3154 | |||
3155 | if (0 != stat (filename, &buf)) | ||
3156 | { | ||
3157 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", filename); | ||
3158 | return GNUNET_OK; | ||
3159 | } | ||
3160 | if (S_ISDIR (buf.st_mode)) | ||
3161 | return GNUNET_OK; /* skip directories */ | ||
3162 | set = get_serialization_short_name (filename); | ||
3163 | rh = GNUNET_BIO_read_open_file (filename); | ||
3164 | if (NULL == rh) | ||
3165 | { | ||
3166 | if (NULL != set) | ||
3167 | { | ||
3168 | GNUNET_FS_remove_sync_file_ (h, GNUNET_FS_SYNC_PATH_MASTER_SEARCH, set); | ||
3169 | GNUNET_free (set); | ||
3170 | } | ||
3171 | return GNUNET_OK; | ||
3172 | } | ||
3173 | sc = deserialize_search (h, rh, NULL, set); | ||
3174 | if (NULL != sc) | ||
3175 | sc->top = GNUNET_FS_make_top (h, &GNUNET_FS_search_signal_suspend_, sc); | ||
3176 | GNUNET_free (set); | ||
3177 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
3178 | { | ||
3179 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
3180 | _ ("Failure while resuming search operation `%s': %s\n"), | ||
3181 | filename, | ||
3182 | emsg); | ||
3183 | GNUNET_free (emsg); | ||
3184 | } | ||
3185 | return GNUNET_OK; | ||
3186 | } | ||
3187 | |||
3188 | |||
3189 | /** | ||
3190 | * Function called with a filename of serialized download operation | ||
3191 | * to deserialize. | ||
3192 | * | ||
3193 | * @param cls the `struct GNUNET_FS_Handle *` | ||
3194 | * @param filename complete filename (absolute path) | ||
3195 | * @return #GNUNET_OK (continue to iterate) | ||
3196 | */ | ||
3197 | static int | ||
3198 | deserialize_download_file (void *cls, const char *filename) | ||
3199 | { | ||
3200 | struct GNUNET_FS_Handle *h = cls; | ||
3201 | char *set; | ||
3202 | char *emsg; | ||
3203 | struct GNUNET_BIO_ReadHandle *rh; | ||
3204 | |||
3205 | set = get_serialization_short_name (filename); | ||
3206 | rh = GNUNET_BIO_read_open_file (filename); | ||
3207 | if (NULL == rh) | ||
3208 | { | ||
3209 | if (0 != unlink (filename)) | ||
3210 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", filename); | ||
3211 | GNUNET_free (set); | ||
3212 | return GNUNET_OK; | ||
3213 | } | ||
3214 | deserialize_download (h, rh, NULL, NULL, set); | ||
3215 | GNUNET_free (set); | ||
3216 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
3217 | { | ||
3218 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
3219 | _ ("Failure while resuming download operation `%s': %s\n"), | ||
3220 | filename, | ||
3221 | emsg); | ||
3222 | GNUNET_free (emsg); | ||
3223 | } | ||
3224 | return GNUNET_OK; | ||
3225 | } | ||
3226 | |||
3227 | |||
3228 | /** | ||
3229 | * Deserialize information about pending operations. | ||
3230 | * | ||
3231 | * @param master_path which master directory should be scanned | ||
3232 | * @param proc function to call for each entry (will get @a h for 'cls') | ||
3233 | * @param h the `struct GNUNET_FS_Handle *` | ||
3234 | */ | ||
3235 | static void | ||
3236 | deserialization_master (const char *master_path, | ||
3237 | GNUNET_FileNameCallback proc, | ||
3238 | struct GNUNET_FS_Handle *h) | ||
3239 | { | ||
3240 | char *dn; | ||
3241 | |||
3242 | dn = get_serialization_file_name (h, master_path, ""); | ||
3243 | if (NULL == dn) | ||
3244 | return; | ||
3245 | if (GNUNET_YES == GNUNET_DISK_directory_test (dn, GNUNET_YES)) | ||
3246 | GNUNET_DISK_directory_scan (dn, proc, h); | ||
3247 | GNUNET_free (dn); | ||
3248 | } | ||
3249 | |||
3250 | |||
3251 | /** | ||
3252 | * Setup a connection to the file-sharing service. | ||
3253 | * | ||
3254 | * @param cfg configuration to use | ||
3255 | * @param client_name unique identifier for this client | ||
3256 | * @param upcb function to call to notify about FS actions | ||
3257 | * @param upcb_cls closure for @a upcb | ||
3258 | * @param flags specific attributes for fs-operations | ||
3259 | * @param ... list of optional options, terminated with #GNUNET_FS_OPTIONS_END | ||
3260 | * @return NULL on error | ||
3261 | */ | ||
3262 | struct GNUNET_FS_Handle * | ||
3263 | GNUNET_FS_start (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
3264 | const char *client_name, | ||
3265 | GNUNET_FS_ProgressCallback upcb, | ||
3266 | void *upcb_cls, | ||
3267 | enum GNUNET_FS_Flags flags, | ||
3268 | ...) | ||
3269 | { | ||
3270 | struct GNUNET_FS_Handle *ret; | ||
3271 | enum GNUNET_FS_OPTIONS opt; | ||
3272 | va_list ap; | ||
3273 | |||
3274 | ret = GNUNET_new (struct GNUNET_FS_Handle); | ||
3275 | ret->cfg = cfg; | ||
3276 | ret->client_name = GNUNET_strdup (client_name); | ||
3277 | ret->upcb = upcb; | ||
3278 | ret->upcb_cls = upcb_cls; | ||
3279 | ret->flags = flags; | ||
3280 | ret->max_parallel_downloads = DEFAULT_MAX_PARALLEL_DOWNLOADS; | ||
3281 | ret->max_parallel_requests = DEFAULT_MAX_PARALLEL_REQUESTS; | ||
3282 | ret->avg_block_latency = | ||
3283 | GNUNET_TIME_UNIT_MINUTES; /* conservative starting point */ | ||
3284 | va_start (ap, flags); | ||
3285 | while (GNUNET_FS_OPTIONS_END != | ||
3286 | (opt = GNUNET_VA_ARG_ENUM (ap, GNUNET_FS_OPTIONS))) | ||
3287 | { | ||
3288 | switch (opt) | ||
3289 | { | ||
3290 | case GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM: | ||
3291 | ret->max_parallel_downloads = va_arg (ap, unsigned int); | ||
3292 | |||
3293 | break; | ||
3294 | |||
3295 | case GNUNET_FS_OPTIONS_REQUEST_PARALLELISM: | ||
3296 | ret->max_parallel_requests = va_arg (ap, unsigned int); | ||
3297 | |||
3298 | break; | ||
3299 | |||
3300 | default: | ||
3301 | GNUNET_break (0); | ||
3302 | GNUNET_free (ret->client_name); | ||
3303 | GNUNET_free (ret); | ||
3304 | va_end (ap); | ||
3305 | return NULL; | ||
3306 | } | ||
3307 | } | ||
3308 | va_end (ap); | ||
3309 | if (0 != (GNUNET_FS_FLAGS_PERSISTENCE & flags)) | ||
3310 | { | ||
3311 | deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_PUBLISH, | ||
3312 | &deserialize_publish_file, | ||
3313 | ret); | ||
3314 | deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_SEARCH, | ||
3315 | &deserialize_search_file, | ||
3316 | ret); | ||
3317 | deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD, | ||
3318 | &deserialize_download_file, | ||
3319 | ret); | ||
3320 | deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_UNINDEX, | ||
3321 | &deserialize_unindex_file, | ||
3322 | ret); | ||
3323 | } | ||
3324 | return ret; | ||
3325 | } | ||
3326 | |||
3327 | |||
3328 | /** | ||
3329 | * Close our connection with the file-sharing service. | ||
3330 | * The callback given to #GNUNET_FS_start() will no longer be | ||
3331 | * called after this function returns. | ||
3332 | * This function MUST NOT be called from within the | ||
3333 | * callback itself. | ||
3334 | * | ||
3335 | * @param h handle that was returned from #GNUNET_FS_start() | ||
3336 | */ | ||
3337 | void | ||
3338 | GNUNET_FS_stop (struct GNUNET_FS_Handle *h) | ||
3339 | { | ||
3340 | while (NULL != h->top_head) | ||
3341 | h->top_head->ssf (h->top_head->ssf_cls); | ||
3342 | if (NULL != h->queue_job) | ||
3343 | GNUNET_SCHEDULER_cancel (h->queue_job); | ||
3344 | GNUNET_free (h->client_name); | ||
3345 | GNUNET_free (h); | ||
3346 | } | ||
3347 | |||
3348 | |||
3349 | /* end of fs_api.c */ | ||