error handling
[oweals/gnunet.git] / src / fs / fs_api.c
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_non_null (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 emtpy 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 emtpy 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 emtpy 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 (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 emtpy 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 (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 emtpy 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 (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, 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, &dur.rel_value_us))
867     return GNUNET_SYSERR;
868   *timestamp = GNUNET_TIME_absolute_subtract (GNUNET_TIME_absolute_get (), dur);
869   return GNUNET_OK;
870 }
871
872
873 /**
874  * Using the given serialization filename, try to deserialize
875  * the file-information tree associated with it.
876  *
877  * @param h master context
878  * @param filename name of the file (without directory) with
879  *        the infromation
880  * @return NULL on error
881  */
882 static struct GNUNET_FS_FileInformation *
883 deserialize_file_information (struct GNUNET_FS_Handle *h, const char *filename);
884
885
886 /**
887  * Using the given serialization filename, try to deserialize
888  * the file-information tree associated with it.
889  *
890  * @param h master context
891  * @param fn name of the file (without directory) with
892  *        the infromation
893  * @param rh handle for reading
894  * @return NULL on error
895  */
896 static struct GNUNET_FS_FileInformation *
897 deserialize_fi_node (struct GNUNET_FS_Handle *h,
898                      const char *fn,
899                      struct GNUNET_BIO_ReadHandle *rh)
900 {
901   struct GNUNET_FS_FileInformation *ret;
902   struct GNUNET_FS_FileInformation *nxt;
903   char b;
904   char *ksks;
905   char *chks;
906   char *skss;
907   char *filename;
908   uint32_t dsize;
909
910   if (GNUNET_OK != GNUNET_BIO_read (rh, "status flag", &b, sizeof(b)))
911   {
912     GNUNET_break (0);
913     return NULL;
914   }
915   ret = GNUNET_new (struct GNUNET_FS_FileInformation);
916   ret->h = h;
917   ksks = NULL;
918   chks = NULL;
919   skss = NULL;
920   filename = NULL;
921   if ((GNUNET_OK != GNUNET_BIO_read_meta_data (rh, "metadata", &ret->meta)) ||
922       (GNUNET_OK != GNUNET_BIO_read_string (rh, "ksk-uri", &ksks, 32 * 1024)) ||
923       ((NULL != ksks) &&
924        ((NULL == (ret->keywords = GNUNET_FS_uri_parse (ksks, NULL))) ||
925         (GNUNET_YES != GNUNET_FS_uri_test_ksk (ret->keywords)))) ||
926       (GNUNET_OK != GNUNET_BIO_read_string (rh, "chk-uri", &chks, 1024)) ||
927       ((NULL != chks) &&
928        ((NULL == (ret->chk_uri = GNUNET_FS_uri_parse (chks, NULL))) ||
929         (GNUNET_YES != GNUNET_FS_uri_test_chk (ret->chk_uri)))) ||
930       (GNUNET_OK != GNUNET_BIO_read_string (rh, "sks-uri", &skss, 1024)) ||
931       ((NULL != skss) &&
932        ((NULL == (ret->sks_uri = GNUNET_FS_uri_parse (skss, NULL))) ||
933         (GNUNET_YES != GNUNET_FS_uri_test_sks (ret->sks_uri)))) ||
934       (GNUNET_OK != read_start_time (rh, &ret->start_time)) ||
935       (GNUNET_OK !=
936        GNUNET_BIO_read_string (rh, "emsg", &ret->emsg, 16 * 1024)) ||
937       (GNUNET_OK !=
938        GNUNET_BIO_read_string (rh, "fn", &ret->filename, 16 * 1024)) ||
939       (GNUNET_OK !=
940        GNUNET_BIO_read_int64 (rh, &ret->bo.expiration_time.abs_value_us)) ||
941       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &ret->bo.anonymity_level)) ||
942       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &ret->bo.content_priority)) ||
943       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &ret->bo.replication_level)))
944   {
945     GNUNET_break (0);
946     goto cleanup;
947   }
948   switch (b)
949   {
950   case 0:   /* file-insert */
951     if (GNUNET_OK != GNUNET_BIO_read_int64 (rh, &ret->data.file.file_size))
952     {
953       GNUNET_break (0);
954       goto cleanup;
955     }
956     ret->is_directory = GNUNET_NO;
957     ret->data.file.do_index = GNUNET_NO;
958     ret->data.file.have_hash = GNUNET_NO;
959     ret->data.file.index_start_confirmed = GNUNET_NO;
960     if (GNUNET_NO == ret->is_published)
961     {
962       if (NULL == ret->filename)
963       {
964         ret->data.file.reader = &GNUNET_FS_data_reader_copy_;
965         ret->data.file.reader_cls =
966           GNUNET_malloc_large (ret->data.file.file_size);
967         if (ret->data.file.reader_cls == NULL)
968           goto cleanup;
969         if (GNUNET_OK != GNUNET_BIO_read (rh,
970                                           "file-data",
971                                           ret->data.file.reader_cls,
972                                           ret->data.file.file_size))
973         {
974           GNUNET_break (0);
975           goto cleanup;
976         }
977       }
978       else
979       {
980         ret->data.file.reader = &GNUNET_FS_data_reader_file_;
981         ret->data.file.reader_cls =
982           GNUNET_FS_make_file_reader_context_ (ret->filename);
983       }
984     }
985     break;
986
987   case 1:   /* file-index, no hash */
988     if (NULL == ret->filename)
989     {
990       GNUNET_break (0);
991       goto cleanup;
992     }
993     if (GNUNET_OK != GNUNET_BIO_read_int64 (rh, &ret->data.file.file_size))
994     {
995       GNUNET_break (0);
996       goto cleanup;
997     }
998     ret->is_directory = GNUNET_NO;
999     ret->data.file.do_index = GNUNET_YES;
1000     ret->data.file.have_hash = GNUNET_NO;
1001     ret->data.file.index_start_confirmed = GNUNET_NO;
1002     ret->data.file.reader = &GNUNET_FS_data_reader_file_;
1003     ret->data.file.reader_cls =
1004       GNUNET_FS_make_file_reader_context_ (ret->filename);
1005     break;
1006
1007   case 2:   /* file-index-with-hash */
1008     if (NULL == ret->filename)
1009     {
1010       GNUNET_break (0);
1011       goto cleanup;
1012     }
1013     if ((GNUNET_OK != GNUNET_BIO_read_int64 (rh, &ret->data.file.file_size)) ||
1014         (GNUNET_OK != GNUNET_BIO_read (rh,
1015                                        "fileid",
1016                                        &ret->data.file.file_id,
1017                                        sizeof(struct GNUNET_HashCode))))
1018     {
1019       GNUNET_break (0);
1020       goto cleanup;
1021     }
1022     ret->is_directory = GNUNET_NO;
1023     ret->data.file.do_index = GNUNET_YES;
1024     ret->data.file.have_hash = GNUNET_YES;
1025     ret->data.file.index_start_confirmed = GNUNET_NO;
1026     ret->data.file.reader = &GNUNET_FS_data_reader_file_;
1027     ret->data.file.reader_cls =
1028       GNUNET_FS_make_file_reader_context_ (ret->filename);
1029     break;
1030
1031   case 3:   /* file-index-with-hash-confirmed */
1032     if (NULL == ret->filename)
1033     {
1034       GNUNET_break (0);
1035       goto cleanup;
1036     }
1037     if ((GNUNET_OK != GNUNET_BIO_read_int64 (rh, &ret->data.file.file_size)) ||
1038         (GNUNET_OK != GNUNET_BIO_read (rh,
1039                                        "fileid",
1040                                        &ret->data.file.file_id,
1041                                        sizeof(struct GNUNET_HashCode))))
1042     {
1043       GNUNET_break (0);
1044       goto cleanup;
1045     }
1046     ret->is_directory = GNUNET_NO;
1047     ret->data.file.do_index = GNUNET_YES;
1048     ret->data.file.have_hash = GNUNET_YES;
1049     ret->data.file.index_start_confirmed = GNUNET_YES;
1050     ret->data.file.reader = &GNUNET_FS_data_reader_file_;
1051     ret->data.file.reader_cls =
1052       GNUNET_FS_make_file_reader_context_ (ret->filename);
1053     break;
1054
1055   case 4:   /* directory */
1056     ret->is_directory = GNUNET_YES;
1057     if ((GNUNET_OK != GNUNET_BIO_read_int32 (rh, &dsize)) ||
1058         (GNUNET_OK !=
1059          GNUNET_BIO_read_int64 (rh, &ret->data.dir.contents_completed)) ||
1060         (GNUNET_OK !=
1061          GNUNET_BIO_read_int64 (rh, &ret->data.dir.contents_size)) ||
1062         (NULL == (ret->data.dir.dir_data = GNUNET_malloc_large (dsize))) ||
1063         (GNUNET_OK !=
1064          GNUNET_BIO_read (rh, "dir-data", ret->data.dir.dir_data, dsize)) ||
1065         (GNUNET_OK !=
1066          GNUNET_BIO_read_string (rh, "ent-filename", &filename, 16 * 1024)))
1067     {
1068       GNUNET_break (0);
1069       goto cleanup;
1070     }
1071     ret->data.dir.dir_size = (uint32_t) dsize;
1072     if (NULL != filename)
1073     {
1074       ret->data.dir.entries = deserialize_file_information (h, filename);
1075       GNUNET_free (filename);
1076       filename = NULL;
1077       nxt = ret->data.dir.entries;
1078       while (NULL != nxt)
1079       {
1080         nxt->dir = ret;
1081         nxt = nxt->next;
1082       }
1083     }
1084     break;
1085
1086   default:
1087     GNUNET_break (0);
1088     goto cleanup;
1089   }
1090   ret->serialization = GNUNET_strdup (fn);
1091   if (GNUNET_OK !=
1092       GNUNET_BIO_read_string (rh, "nxt-filename", &filename, 16 * 1024))
1093   {
1094     GNUNET_break (0);
1095     goto cleanup;
1096   }
1097   if (NULL != filename)
1098   {
1099     ret->next = deserialize_file_information (h, filename);
1100     GNUNET_free (filename);
1101     filename = NULL;
1102   }
1103   GNUNET_free_non_null (ksks);
1104   GNUNET_free_non_null (skss);
1105   GNUNET_free_non_null (chks);
1106   return ret;
1107 cleanup:
1108   GNUNET_free_non_null (ksks);
1109   GNUNET_free_non_null (chks);
1110   GNUNET_free_non_null (skss);
1111   GNUNET_free_non_null (filename);
1112   GNUNET_FS_file_information_destroy (ret, NULL, NULL);
1113   return NULL;
1114 }
1115
1116
1117 /**
1118  * Using the given serialization filename, try to deserialize
1119  * the file-information tree associated with it.
1120  *
1121  * @param h master context
1122  * @param filename name of the file (without directory) with
1123  *        the infromation
1124  * @return NULL on error
1125  */
1126 static struct GNUNET_FS_FileInformation *
1127 deserialize_file_information (struct GNUNET_FS_Handle *h, const char *filename)
1128 {
1129   struct GNUNET_FS_FileInformation *ret;
1130   struct GNUNET_BIO_ReadHandle *rh;
1131   char *emsg;
1132   char *fn;
1133
1134   rh = get_read_handle (h, GNUNET_FS_SYNC_PATH_FILE_INFO, filename);
1135   if (NULL == rh)
1136     return NULL;
1137   ret = deserialize_fi_node (h, filename, rh);
1138   if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
1139   {
1140     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1141                 _ ("Failed to resume publishing information `%s': %s\n"),
1142                 filename,
1143                 emsg);
1144     GNUNET_free (emsg);
1145   }
1146   if (NULL == ret)
1147   {
1148     fn =
1149       get_serialization_file_name (h, GNUNET_FS_SYNC_PATH_FILE_INFO, filename);
1150     if (NULL != fn)
1151     {
1152       if (0 != unlink (fn))
1153         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
1154       GNUNET_free (fn);
1155     }
1156   }
1157   return ret;
1158 }
1159
1160
1161 /**
1162  * Given a serialization name (full absolute path), return the
1163  * basename of the file (without the path), which must only
1164  * consist of the 6 random characters.
1165  *
1166  * @param fullname name to extract the basename from
1167  * @return copy of the basename, NULL on error
1168  */
1169 static char *
1170 get_serialization_short_name (const char *fullname)
1171 {
1172   const char *end;
1173   const char *nxt;
1174
1175   end = NULL;
1176   nxt = fullname;
1177   /* FIXME: we could do this faster since we know
1178    * the length of 'end'... */
1179   while ('\0' != *nxt)
1180   {
1181     if (DIR_SEPARATOR == *nxt)
1182       end = nxt + 1;
1183     nxt++;
1184   }
1185   if ((NULL == end) || (0 == strlen (end)))
1186   {
1187     GNUNET_break (0);
1188     return NULL;
1189   }
1190   GNUNET_break (6 == strlen (end));
1191   return GNUNET_strdup (end);
1192 }
1193
1194
1195 /**
1196  * Create a new random name for serialization.  Also checks if persistence
1197  * is enabled and returns NULL if not.
1198  *
1199  * @param h master context
1200  * @param ext component of the path
1201  * @return NULL on errror
1202  */
1203 static char *
1204 make_serialization_file_name (struct GNUNET_FS_Handle *h, const char *ext)
1205 {
1206   char *fn;
1207   char *dn;
1208   char *ret;
1209
1210   if (0 == (h->flags & GNUNET_FS_FLAGS_PERSISTENCE))
1211     return NULL; /* persistence not requested */
1212   dn = get_serialization_file_name (h, ext, "");
1213   if (NULL == dn)
1214     return NULL;
1215   if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (dn))
1216   {
1217     GNUNET_free (dn);
1218     return NULL;
1219   }
1220   fn = GNUNET_DISK_mktemp (dn);
1221   GNUNET_free (dn);
1222   if (NULL == fn)
1223     return NULL; /* epic fail */
1224   ret = get_serialization_short_name (fn);
1225   GNUNET_free (fn);
1226   return ret;
1227 }
1228
1229
1230 /**
1231  * Create a new random name for serialization.  Also checks if persistence
1232  * is enabled and returns NULL if not.
1233  *
1234  * @param h master context
1235  * @param ext component of the path
1236  * @param uni name of parent
1237  * @return NULL on errror
1238  */
1239 static char *
1240 make_serialization_file_name_in_dir (struct GNUNET_FS_Handle *h,
1241                                      const char *ext,
1242                                      const char *uni)
1243 {
1244   char *fn;
1245   char *dn;
1246   char *ret;
1247
1248   if (0 == (h->flags & GNUNET_FS_FLAGS_PERSISTENCE))
1249     return NULL; /* persistence not requested */
1250   dn = get_serialization_file_name_in_dir (h, ext, uni, "");
1251   if (NULL == dn)
1252     return NULL;
1253   if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (dn))
1254   {
1255     GNUNET_free (dn);
1256     return NULL;
1257   }
1258   fn = GNUNET_DISK_mktemp (dn);
1259   GNUNET_free (dn);
1260   if (NULL == fn)
1261     return NULL; /* epic fail */
1262   ret = get_serialization_short_name (fn);
1263   GNUNET_free (fn);
1264   return ret;
1265 }
1266
1267
1268 /**
1269  * Copy all of the data from the reader to the write handle.
1270  *
1271  * @param wh write handle
1272  * @param fi file with reader
1273  * @return #GNUNET_OK on success
1274  */
1275 static int
1276 copy_from_reader (struct GNUNET_BIO_WriteHandle *wh,
1277                   struct GNUNET_FS_FileInformation *fi)
1278 {
1279   char buf[32 * 1024];
1280   uint64_t off;
1281   size_t ret;
1282   size_t left;
1283   char *emsg;
1284
1285   emsg = NULL;
1286   off = 0;
1287   while (off < fi->data.file.file_size)
1288   {
1289     left = GNUNET_MIN (sizeof(buf), fi->data.file.file_size - off);
1290     ret =
1291       fi->data.file.reader (fi->data.file.reader_cls, off, left, buf, &emsg);
1292     if (0 == ret)
1293     {
1294       GNUNET_free (emsg);
1295       return GNUNET_SYSERR;
1296     }
1297     if (GNUNET_OK != GNUNET_BIO_write (wh, buf, ret))
1298       return GNUNET_SYSERR;
1299     off += ret;
1300   }
1301   return GNUNET_OK;
1302 }
1303
1304
1305 /**
1306  * Create a temporary file on disk to store the current
1307  * state of @a fi in.
1308  *
1309  * @param fi file information to sync with disk
1310  */
1311 void
1312 GNUNET_FS_file_information_sync_ (struct GNUNET_FS_FileInformation *fi)
1313 {
1314   char *fn;
1315   struct GNUNET_BIO_WriteHandle *wh;
1316   char b;
1317   char *ksks;
1318   char *chks;
1319   char *skss;
1320
1321   if (NULL == fi->serialization)
1322     fi->serialization =
1323       make_serialization_file_name (fi->h, GNUNET_FS_SYNC_PATH_FILE_INFO);
1324   if (NULL == fi->serialization)
1325     return;
1326   wh =
1327     get_write_handle (fi->h, GNUNET_FS_SYNC_PATH_FILE_INFO, fi->serialization);
1328   if (NULL == wh)
1329   {
1330     GNUNET_free (fi->serialization);
1331     fi->serialization = NULL;
1332     return;
1333   }
1334   if (GNUNET_YES == fi->is_directory)
1335     b = 4;
1336   else if (GNUNET_YES == fi->data.file.index_start_confirmed)
1337     b = 3;
1338   else if (GNUNET_YES == fi->data.file.have_hash)
1339     b = 2;
1340   else if (GNUNET_YES == fi->data.file.do_index)
1341     b = 1;
1342   else
1343     b = 0;
1344   if (NULL != fi->keywords)
1345     ksks = GNUNET_FS_uri_to_string (fi->keywords);
1346   else
1347     ksks = NULL;
1348   if (NULL != fi->chk_uri)
1349     chks = GNUNET_FS_uri_to_string (fi->chk_uri);
1350   else
1351     chks = NULL;
1352   if (NULL != fi->sks_uri)
1353     skss = GNUNET_FS_uri_to_string (fi->sks_uri);
1354   else
1355     skss = NULL;
1356   if ((GNUNET_OK != GNUNET_BIO_write (wh, &b, sizeof(b))) ||
1357       (GNUNET_OK != GNUNET_BIO_write_meta_data (wh, fi->meta)) ||
1358       (GNUNET_OK != GNUNET_BIO_write_string (wh, ksks)) ||
1359       (GNUNET_OK != GNUNET_BIO_write_string (wh, chks)) ||
1360       (GNUNET_OK != GNUNET_BIO_write_string (wh, skss)) ||
1361       (GNUNET_OK != write_start_time (wh, fi->start_time)) ||
1362       (GNUNET_OK != GNUNET_BIO_write_string (wh, fi->emsg)) ||
1363       (GNUNET_OK != GNUNET_BIO_write_string (wh, fi->filename)) ||
1364       (GNUNET_OK !=
1365        GNUNET_BIO_write_int64 (wh, fi->bo.expiration_time.abs_value_us)) ||
1366       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, fi->bo.anonymity_level)) ||
1367       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, fi->bo.content_priority)) ||
1368       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, fi->bo.replication_level)))
1369   {
1370     GNUNET_break (0);
1371     goto cleanup;
1372   }
1373   GNUNET_free_non_null (chks);
1374   chks = NULL;
1375   GNUNET_free_non_null (ksks);
1376   ksks = NULL;
1377   GNUNET_free_non_null (skss);
1378   skss = NULL;
1379
1380   switch (b)
1381   {
1382   case 0:   /* file-insert */
1383     if (GNUNET_OK != GNUNET_BIO_write_int64 (wh, fi->data.file.file_size))
1384     {
1385       GNUNET_break (0);
1386       goto cleanup;
1387     }
1388     if ((GNUNET_NO == fi->is_published) && (NULL == fi->filename))
1389       if (GNUNET_OK != copy_from_reader (wh, fi))
1390       {
1391         GNUNET_break (0);
1392         goto cleanup;
1393       }
1394     break;
1395
1396   case 1:   /* file-index, no hash */
1397     if (NULL == fi->filename)
1398     {
1399       GNUNET_break (0);
1400       goto cleanup;
1401     }
1402     if (GNUNET_OK != GNUNET_BIO_write_int64 (wh, fi->data.file.file_size))
1403     {
1404       GNUNET_break (0);
1405       goto cleanup;
1406     }
1407     break;
1408
1409   case 2:   /* file-index-with-hash */
1410   case 3:   /* file-index-with-hash-confirmed */
1411     if (NULL == fi->filename)
1412     {
1413       GNUNET_break (0);
1414       goto cleanup;
1415     }
1416     if ((GNUNET_OK != GNUNET_BIO_write_int64 (wh, fi->data.file.file_size)) ||
1417         (GNUNET_OK != GNUNET_BIO_write (wh,
1418                                         &fi->data.file.file_id,
1419                                         sizeof(struct GNUNET_HashCode))))
1420     {
1421       GNUNET_break (0);
1422       goto cleanup;
1423     }
1424     break;
1425
1426   case 4:   /* directory */
1427     if ((NULL != fi->data.dir.entries) &&
1428         (NULL == fi->data.dir.entries->serialization))
1429       GNUNET_FS_file_information_sync_ (fi->data.dir.entries);
1430     if ((GNUNET_OK != GNUNET_BIO_write_int32 (wh, fi->data.dir.dir_size)) ||
1431         (GNUNET_OK !=
1432          GNUNET_BIO_write_int64 (wh, fi->data.dir.contents_completed)) ||
1433         (GNUNET_OK !=
1434          GNUNET_BIO_write_int64 (wh, fi->data.dir.contents_size)) ||
1435         (GNUNET_OK != GNUNET_BIO_write (wh,
1436                                         fi->data.dir.dir_data,
1437                                         (uint32_t) fi->data.dir.dir_size)) ||
1438         (GNUNET_OK !=
1439          GNUNET_BIO_write_string (wh,
1440                                   (fi->data.dir.entries == NULL)
1441                                   ? NULL
1442                                   : fi->data.dir.entries->serialization)))
1443     {
1444       GNUNET_break (0);
1445       goto cleanup;
1446     }
1447     break;
1448
1449   default:
1450     GNUNET_assert (0);
1451     goto cleanup;
1452   }
1453   if ((NULL != fi->next) && (NULL == fi->next->serialization))
1454     GNUNET_FS_file_information_sync_ (fi->next);
1455   if (GNUNET_OK != GNUNET_BIO_write_string (wh,
1456                                             (fi->next != NULL)
1457                                             ? fi->next->serialization
1458                                             : NULL))
1459   {
1460     GNUNET_break (0);
1461     goto cleanup;
1462   }
1463   if (GNUNET_OK != GNUNET_BIO_write_close (wh))
1464   {
1465     wh = NULL;
1466     GNUNET_break (0);
1467     goto cleanup;
1468   }
1469   return; /* done! */
1470 cleanup:
1471   if (NULL != wh)
1472     (void) GNUNET_BIO_write_close (wh);
1473   GNUNET_free_non_null (chks);
1474   GNUNET_free_non_null (ksks);
1475   GNUNET_free_non_null (skss);
1476   fn = get_serialization_file_name (fi->h,
1477                                     GNUNET_FS_SYNC_PATH_FILE_INFO,
1478                                     fi->serialization);
1479   if (NULL != fn)
1480   {
1481     if (0 != unlink (fn))
1482       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
1483     GNUNET_free (fn);
1484   }
1485   GNUNET_free (fi->serialization);
1486   fi->serialization = NULL;
1487 }
1488
1489
1490 /**
1491  * Find the entry in the file information struct where the
1492  * serialization filename matches the given name.
1493  *
1494  * @param pos file information to search
1495  * @param srch filename to search for
1496  * @return NULL if srch was not found in this subtree
1497  */
1498 static struct GNUNET_FS_FileInformation *
1499 find_file_position (struct GNUNET_FS_FileInformation *pos, const char *srch)
1500 {
1501   struct GNUNET_FS_FileInformation *r;
1502
1503   while (NULL != pos)
1504   {
1505     if (0 == strcmp (srch, pos->serialization))
1506       return pos;
1507     if ((GNUNET_YES == pos->is_directory) &&
1508         (NULL != (r = find_file_position (pos->data.dir.entries, srch))))
1509       return r;
1510     pos = pos->next;
1511   }
1512   return NULL;
1513 }
1514
1515
1516 /**
1517  * Signal the FS's progress function that we are resuming
1518  * an upload.
1519  *
1520  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`, for the parent (!))
1521  * @param fi the entry in the publish-structure
1522  * @param length length of the file or directory
1523  * @param meta metadata for the file or directory (can be modified)
1524  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1525  * @param bo block options (can be modified)
1526  * @param do_index should we index?
1527  * @param client_info pointer to client context set upon creation (can be modified)
1528  * @return #GNUNET_OK to continue (always)
1529  */
1530 static int
1531 fip_signal_resume (void *cls,
1532                    struct GNUNET_FS_FileInformation *fi,
1533                    uint64_t length,
1534                    struct GNUNET_CONTAINER_MetaData *meta,
1535                    struct GNUNET_FS_Uri **uri,
1536                    struct GNUNET_FS_BlockOptions *bo,
1537                    int *do_index,
1538                    void **client_info)
1539 {
1540   struct GNUNET_FS_PublishContext *pc = cls;
1541   struct GNUNET_FS_ProgressInfo pi;
1542
1543   if (GNUNET_YES == pc->skip_next_fi_callback)
1544   {
1545     pc->skip_next_fi_callback = GNUNET_NO;
1546     return GNUNET_OK;
1547   }
1548   pi.status = GNUNET_FS_STATUS_PUBLISH_RESUME;
1549   pi.value.publish.specifics.resume.message = fi->emsg;
1550   pi.value.publish.specifics.resume.chk_uri = fi->chk_uri;
1551   *client_info = GNUNET_FS_publish_make_status_ (&pi, pc, fi, 0);
1552   if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta))
1553   {
1554     /* process entries in directory */
1555     pc->skip_next_fi_callback = GNUNET_YES;
1556     GNUNET_FS_file_information_inspect (fi, &fip_signal_resume, pc);
1557   }
1558   return GNUNET_OK;
1559 }
1560
1561
1562 /**
1563  * Function called with a filename of serialized publishing operation
1564  * to deserialize.
1565  *
1566  * @param cls the `struct GNUNET_FS_Handle *`
1567  * @param filename complete filename (absolute path)
1568  * @return #GNUNET_OK (continue to iterate)
1569  */
1570 static int
1571 deserialize_publish_file (void *cls, const char *filename)
1572 {
1573   struct GNUNET_FS_Handle *h = cls;
1574   struct GNUNET_BIO_ReadHandle *rh;
1575   struct GNUNET_FS_PublishContext *pc;
1576   int32_t options;
1577   int32_t all_done;
1578   int32_t have_ns;
1579   char *fi_root;
1580   struct GNUNET_CRYPTO_EcdsaPrivateKey ns;
1581   char *fi_pos;
1582   char *emsg;
1583
1584   pc = GNUNET_new (struct GNUNET_FS_PublishContext);
1585   pc->h = h;
1586   pc->serialization = get_serialization_short_name (filename);
1587   fi_root = NULL;
1588   fi_pos = NULL;
1589   rh = GNUNET_BIO_read_open (filename);
1590   if (NULL == rh)
1591   {
1592     GNUNET_break (0);
1593     goto cleanup;
1594   }
1595   if ((GNUNET_OK !=
1596        GNUNET_BIO_read_string (rh, "publish-nid", &pc->nid, 1024)) ||
1597       (GNUNET_OK !=
1598        GNUNET_BIO_read_string (rh, "publish-nuid", &pc->nuid, 1024)) ||
1599       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &options)) ||
1600       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &all_done)) ||
1601       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &have_ns)) ||
1602       (GNUNET_OK !=
1603        GNUNET_BIO_read_string (rh, "publish-firoot", &fi_root, 128)) ||
1604       (GNUNET_OK !=
1605        GNUNET_BIO_read_string (rh, "publish-fipos", &fi_pos, 128)) ||
1606       ((GNUNET_YES == have_ns) &&
1607        (GNUNET_OK != GNUNET_BIO_read (rh, "publish-ns", &ns, sizeof(ns)))))
1608   {
1609     GNUNET_break (0);
1610     goto cleanup;
1611   }
1612   pc->options = options;
1613   pc->all_done = all_done;
1614   if (NULL == fi_root)
1615   {
1616     GNUNET_break (0);
1617     goto cleanup;
1618   }
1619   pc->fi = deserialize_file_information (h, fi_root);
1620   if (NULL == pc->fi)
1621   {
1622     GNUNET_break (0);
1623     goto cleanup;
1624   }
1625   if (GNUNET_YES == have_ns)
1626   {
1627     pc->ns = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey);
1628     *pc->ns = ns;
1629   }
1630   if ((0 == (pc->options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY)) &&
1631       (GNUNET_YES != pc->all_done))
1632   {
1633     pc->dsh = GNUNET_DATASTORE_connect (h->cfg);
1634     if (NULL == pc->dsh)
1635       goto cleanup;
1636   }
1637   if (NULL != fi_pos)
1638   {
1639     pc->fi_pos = find_file_position (pc->fi, fi_pos);
1640     GNUNET_free (fi_pos);
1641     fi_pos = NULL;
1642     if (NULL == pc->fi_pos)
1643     {
1644       /* failed to find position for resuming, outch! Will start from root! */
1645       GNUNET_break (0);
1646       if (GNUNET_YES != pc->all_done)
1647         pc->fi_pos = pc->fi;
1648     }
1649   }
1650   GNUNET_free (fi_root);
1651   fi_root = NULL;
1652   /* generate RESUME event(s) */
1653   GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_resume, pc);
1654
1655   /* re-start publishing (if needed)... */
1656   if (GNUNET_YES != pc->all_done)
1657   {
1658     GNUNET_assert (NULL == pc->upload_task);
1659     pc->upload_task =
1660       GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
1661                                           &GNUNET_FS_publish_main_,
1662                                           pc);
1663   }
1664   if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
1665   {
1666     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1667                 _ ("Failure while resuming publishing operation `%s': %s\n"),
1668                 filename,
1669                 emsg);
1670     GNUNET_free (emsg);
1671   }
1672   pc->top = GNUNET_FS_make_top (h, &GNUNET_FS_publish_signal_suspend_, pc);
1673   return GNUNET_OK;
1674 cleanup:
1675   GNUNET_free_non_null (pc->nid);
1676   GNUNET_free_non_null (pc->nuid);
1677   GNUNET_free_non_null (fi_root);
1678   GNUNET_free_non_null (fi_pos);
1679   if ((NULL != rh) && (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)))
1680   {
1681     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1682                 _ ("Failed to resume publishing operation `%s': %s\n"),
1683                 filename,
1684                 emsg);
1685     GNUNET_free (emsg);
1686   }
1687   if (NULL != pc->fi)
1688     GNUNET_FS_file_information_destroy (pc->fi, NULL, NULL);
1689   if (0 != unlink (filename))
1690     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", filename);
1691   GNUNET_free (pc->serialization);
1692   GNUNET_free (pc);
1693   return GNUNET_OK;
1694 }
1695
1696
1697 /**
1698  * Synchronize this publishing struct with its mirror
1699  * on disk.  Note that all internal FS-operations that change
1700  * publishing structs should already call "sync" internally,
1701  * so this function is likely not useful for clients.
1702  *
1703  * @param pc the struct to sync
1704  */
1705 void
1706 GNUNET_FS_publish_sync_ (struct GNUNET_FS_PublishContext *pc)
1707 {
1708   struct GNUNET_BIO_WriteHandle *wh;
1709   int32_t have_ns;
1710
1711   if (NULL == pc->serialization)
1712     pc->serialization =
1713       make_serialization_file_name (pc->h, GNUNET_FS_SYNC_PATH_MASTER_PUBLISH);
1714   if (NULL == pc->serialization)
1715     return;
1716   if (NULL == pc->fi)
1717     return;
1718   if (NULL == pc->fi->serialization)
1719   {
1720     GNUNET_break (0);
1721     return;
1722   }
1723   wh = get_write_handle (pc->h,
1724                          GNUNET_FS_SYNC_PATH_MASTER_PUBLISH,
1725                          pc->serialization);
1726   if (NULL == wh)
1727   {
1728     GNUNET_break (0);
1729     goto cleanup;
1730   }
1731   have_ns = (NULL != pc->ns) ? GNUNET_YES : GNUNET_NO;
1732   if ((GNUNET_OK != GNUNET_BIO_write_string (wh, pc->nid)) ||
1733       (GNUNET_OK != GNUNET_BIO_write_string (wh, pc->nuid)) ||
1734       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, pc->options)) ||
1735       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, pc->all_done)) ||
1736       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, have_ns)) ||
1737       (GNUNET_OK != GNUNET_BIO_write_string (wh, pc->fi->serialization)) ||
1738       (GNUNET_OK != GNUNET_BIO_write_string (wh,
1739                                              (NULL == pc->fi_pos)
1740                                              ? NULL
1741                                              : pc->fi_pos->serialization)) ||
1742       ((NULL != pc->ns) &&
1743        (GNUNET_OK !=
1744         GNUNET_BIO_write (wh,
1745                           pc->ns,
1746                           sizeof(struct GNUNET_CRYPTO_EcdsaPrivateKey)))))
1747   {
1748     GNUNET_break (0);
1749     goto cleanup;
1750   }
1751   if (GNUNET_OK != GNUNET_BIO_write_close (wh))
1752   {
1753     wh = NULL;
1754     GNUNET_break (0);
1755     goto cleanup;
1756   }
1757   return;
1758 cleanup:
1759   if (NULL != wh)
1760     (void) GNUNET_BIO_write_close (wh);
1761   GNUNET_FS_remove_sync_file_ (pc->h,
1762                                GNUNET_FS_SYNC_PATH_MASTER_PUBLISH,
1763                                pc->serialization);
1764   GNUNET_free (pc->serialization);
1765   pc->serialization = NULL;
1766 }
1767
1768
1769 /**
1770  * Synchronize this unindex struct with its mirror
1771  * on disk.  Note that all internal FS-operations that change
1772  * publishing structs should already call "sync" internally,
1773  * so this function is likely not useful for clients.
1774  *
1775  * @param uc the struct to sync
1776  */
1777 void
1778 GNUNET_FS_unindex_sync_ (struct GNUNET_FS_UnindexContext *uc)
1779 {
1780   struct GNUNET_BIO_WriteHandle *wh;
1781   char *uris;
1782
1783   if (NULL == uc->serialization)
1784     uc->serialization =
1785       make_serialization_file_name (uc->h, GNUNET_FS_SYNC_PATH_MASTER_UNINDEX);
1786   if (NULL == uc->serialization)
1787     return;
1788   wh = get_write_handle (uc->h,
1789                          GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
1790                          uc->serialization);
1791   if (NULL == wh)
1792   {
1793     GNUNET_break (0);
1794     goto cleanup;
1795   }
1796   if (NULL != uc->ksk_uri)
1797     uris = GNUNET_FS_uri_to_string (uc->ksk_uri);
1798   else
1799     uris = NULL;
1800   if ((GNUNET_OK != GNUNET_BIO_write_string (wh, uc->filename)) ||
1801       (GNUNET_OK != GNUNET_BIO_write_int64 (wh, uc->file_size)) ||
1802       (GNUNET_OK != write_start_time (wh, uc->start_time)) ||
1803       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, (uint32_t) uc->state)) ||
1804       (GNUNET_OK !=
1805        GNUNET_BIO_write (wh, &uc->chk, sizeof(struct ContentHashKey))) ||
1806       (GNUNET_OK != GNUNET_BIO_write_string (wh, uris)) ||
1807       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, (uint32_t) uc->ksk_offset)) ||
1808       ((uc->state == UNINDEX_STATE_FS_NOTIFY) &&
1809        (GNUNET_OK != GNUNET_BIO_write (wh,
1810                                        &uc->file_id,
1811                                        sizeof(struct GNUNET_HashCode)))) ||
1812       ((uc->state == UNINDEX_STATE_ERROR) &&
1813        (GNUNET_OK != GNUNET_BIO_write_string (wh, uc->emsg))))
1814   {
1815     GNUNET_break (0);
1816     goto cleanup;
1817   }
1818   if (GNUNET_OK != GNUNET_BIO_write_close (wh))
1819   {
1820     wh = NULL;
1821     GNUNET_break (0);
1822     goto cleanup;
1823   }
1824   return;
1825 cleanup:
1826   if (NULL != wh)
1827     (void) GNUNET_BIO_write_close (wh);
1828   GNUNET_FS_remove_sync_file_ (uc->h,
1829                                GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
1830                                uc->serialization);
1831   GNUNET_free (uc->serialization);
1832   uc->serialization = NULL;
1833 }
1834
1835
1836 /**
1837  * Serialize a download request.
1838  *
1839  * @param wh handle for writing the download request to disk
1840  * @param dr the the request to write to disk
1841  * @return #GNUNET_YES on success, #GNUNET_NO on error
1842  */
1843 static int
1844 write_download_request (struct GNUNET_BIO_WriteHandle *wh,
1845                         struct DownloadRequest *dr)
1846 {
1847   unsigned int i;
1848
1849   if ((GNUNET_OK != GNUNET_BIO_write_int32 (wh, dr->state)) ||
1850       (GNUNET_OK != GNUNET_BIO_write_int64 (wh, dr->offset)) ||
1851       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, dr->num_children)) ||
1852       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, dr->depth)))
1853     return GNUNET_NO;
1854   if ((BRS_CHK_SET == dr->state) &&
1855       (GNUNET_OK !=
1856        GNUNET_BIO_write (wh, &dr->chk, sizeof(struct ContentHashKey))))
1857     return GNUNET_NO;
1858   for (i = 0; i < dr->num_children; i++)
1859     if (GNUNET_NO == write_download_request (wh, dr->children[i]))
1860       return GNUNET_NO;
1861   return GNUNET_YES;
1862 }
1863
1864
1865 /**
1866  * Read a download request tree.
1867  *
1868  * @param rh cadet to read from
1869  * @return value the download request read from disk, NULL on error
1870  */
1871 static struct DownloadRequest *
1872 read_download_request (struct GNUNET_BIO_ReadHandle *rh)
1873 {
1874   struct DownloadRequest *dr;
1875   unsigned int i;
1876
1877   dr = GNUNET_new (struct DownloadRequest);
1878   if ((GNUNET_OK != GNUNET_BIO_read_int32 (rh, &dr->state)) ||
1879       (GNUNET_OK != GNUNET_BIO_read_int64 (rh, &dr->offset)) ||
1880       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &dr->num_children)) ||
1881       (dr->num_children > CHK_PER_INODE) ||
1882       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &dr->depth)) ||
1883       ((0 == dr->depth) && (dr->num_children > 0)) ||
1884       ((dr->depth > 0) && (0 == dr->num_children)))
1885   {
1886     GNUNET_break (0);
1887     dr->num_children = 0;
1888     goto cleanup;
1889   }
1890   if (dr->num_children > 0)
1891     dr->children =
1892       GNUNET_malloc (dr->num_children * sizeof(struct DownloadRequest *));
1893   switch (dr->state)
1894   {
1895   case BRS_INIT:
1896   case BRS_RECONSTRUCT_DOWN:
1897   case BRS_RECONSTRUCT_META_UP:
1898   case BRS_RECONSTRUCT_UP:
1899     break;
1900
1901   case BRS_CHK_SET:
1902     if (GNUNET_OK !=
1903         GNUNET_BIO_read (rh, "chk", &dr->chk, sizeof(struct ContentHashKey)))
1904       goto cleanup;
1905     break;
1906
1907   case BRS_DOWNLOAD_DOWN:
1908   case BRS_DOWNLOAD_UP:
1909   case BRS_ERROR:
1910     break;
1911
1912   default:
1913     GNUNET_break (0);
1914     goto cleanup;
1915   }
1916   for (i = 0; i < dr->num_children; i++)
1917   {
1918     if (NULL == (dr->children[i] = read_download_request (rh)))
1919       goto cleanup;
1920     dr->children[i]->parent = dr;
1921   }
1922   return dr;
1923 cleanup:
1924   GNUNET_FS_free_download_request_ (dr);
1925   return NULL;
1926 }
1927
1928
1929 /**
1930  * Compute the name of the sync file (or directory) for the given download
1931  * context.
1932  *
1933  * @param dc download context to compute for
1934  * @param uni unique filename to use, use "" for the directory name
1935  * @param ext extension to use, use ".dir" for our own subdirectory
1936  * @return the expanded file name, NULL for none
1937  */
1938 static char *
1939 get_download_sync_filename (struct GNUNET_FS_DownloadContext *dc,
1940                             const char *uni,
1941                             const char *ext)
1942 {
1943   char *par;
1944   char *epar;
1945
1946   if (dc->parent == NULL)
1947     return get_serialization_file_name (dc->h,
1948                                         (dc->search != NULL)
1949                                         ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD
1950                                         : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
1951                                         uni);
1952   if (NULL == dc->parent->serialization)
1953     return NULL;
1954   par = get_download_sync_filename (dc->parent, dc->parent->serialization, "");
1955   if (NULL == par)
1956     return NULL;
1957   GNUNET_asprintf (&epar, "%s.dir%s%s%s", par, DIR_SEPARATOR_STR, uni, ext);
1958   GNUNET_free (par);
1959   return epar;
1960 }
1961
1962
1963 /**
1964  * Synchronize this download struct with its mirror
1965  * on disk.  Note that all internal FS-operations that change
1966  * publishing structs should already call "sync" internally,
1967  * so this function is likely not useful for clients.
1968  *
1969  * @param dc the struct to sync
1970  */
1971 void
1972 GNUNET_FS_download_sync_ (struct GNUNET_FS_DownloadContext *dc)
1973 {
1974   struct GNUNET_BIO_WriteHandle *wh;
1975   char *uris;
1976   char *fn;
1977   char *dir;
1978
1979   if (0 != (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
1980     return; /* we don't sync probes */
1981   if (NULL == dc->serialization)
1982   {
1983     dir = get_download_sync_filename (dc, "", "");
1984     if (NULL == dir)
1985       return;
1986     if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (dir))
1987     {
1988       GNUNET_free (dir);
1989       return;
1990     }
1991     fn = GNUNET_DISK_mktemp (dir);
1992     GNUNET_free (dir);
1993     if (NULL == fn)
1994       return;
1995     dc->serialization = get_serialization_short_name (fn);
1996   }
1997   else
1998   {
1999     fn = get_download_sync_filename (dc, dc->serialization, "");
2000     if (NULL == fn)
2001     {
2002       GNUNET_free (dc->serialization);
2003       dc->serialization = NULL;
2004       GNUNET_free (fn);
2005       return;
2006     }
2007   }
2008   wh = GNUNET_BIO_write_open (fn);
2009   if (NULL == wh)
2010   {
2011     GNUNET_free (dc->serialization);
2012     dc->serialization = NULL;
2013     GNUNET_free (fn);
2014     return;
2015   }
2016   GNUNET_assert ((GNUNET_YES == GNUNET_FS_uri_test_chk (dc->uri)) ||
2017                  (GNUNET_YES == GNUNET_FS_uri_test_loc (dc->uri)));
2018   uris = GNUNET_FS_uri_to_string (dc->uri);
2019   if ((GNUNET_OK != GNUNET_BIO_write_string (wh, uris)) ||
2020       (GNUNET_OK != GNUNET_BIO_write_meta_data (wh, dc->meta)) ||
2021       (GNUNET_OK != GNUNET_BIO_write_string (wh, dc->emsg)) ||
2022       (GNUNET_OK != GNUNET_BIO_write_string (wh, dc->filename)) ||
2023       (GNUNET_OK != GNUNET_BIO_write_string (wh, dc->temp_filename)) ||
2024       (GNUNET_OK != GNUNET_BIO_write_int64 (wh, dc->old_file_size)) ||
2025       (GNUNET_OK != GNUNET_BIO_write_int64 (wh, dc->offset)) ||
2026       (GNUNET_OK != GNUNET_BIO_write_int64 (wh, dc->length)) ||
2027       (GNUNET_OK != GNUNET_BIO_write_int64 (wh, dc->completed)) ||
2028       (GNUNET_OK != write_start_time (wh, dc->start_time)) ||
2029       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, dc->anonymity)) ||
2030       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, (uint32_t) dc->options)) ||
2031       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, (uint32_t) dc->has_finished)))
2032   {
2033     GNUNET_break (0);
2034     goto cleanup;
2035   }
2036   if (NULL == dc->emsg)
2037   {
2038     GNUNET_assert (dc->top_request != NULL);
2039     if (GNUNET_YES != write_download_request (wh, dc->top_request))
2040     {
2041       GNUNET_break (0);
2042       goto cleanup;
2043     }
2044   }
2045   GNUNET_free_non_null (uris);
2046   uris = NULL;
2047   if (GNUNET_OK != GNUNET_BIO_write_close (wh))
2048   {
2049     wh = NULL;
2050     GNUNET_break (0);
2051     goto cleanup;
2052   }
2053   GNUNET_free (fn);
2054   return;
2055 cleanup:
2056   if (NULL != wh)
2057     (void) GNUNET_BIO_write_close (wh);
2058   GNUNET_free_non_null (uris);
2059   if (0 != unlink (fn))
2060     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
2061   GNUNET_free (fn);
2062   GNUNET_free (dc->serialization);
2063   dc->serialization = NULL;
2064 }
2065
2066
2067 /**
2068  * Synchronize this search result with its mirror
2069  * on disk.  Note that all internal FS-operations that change
2070  * publishing structs should already call "sync" internally,
2071  * so this function is likely not useful for clients.
2072  *
2073  * @param sr the struct to sync
2074  */
2075 void
2076 GNUNET_FS_search_result_sync_ (struct GNUNET_FS_SearchResult *sr)
2077 {
2078   struct GNUNET_BIO_WriteHandle *wh;
2079   char *uris;
2080
2081   if (NULL == sr->sc)
2082     return;
2083   uris = NULL;
2084   if (NULL == sr->serialization)
2085     sr->serialization =
2086       make_serialization_file_name_in_dir (sr->h,
2087                                            (sr->sc->psearch_result == NULL)
2088                                            ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2089                                            : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2090                                            sr->sc->serialization);
2091   if (NULL == sr->serialization)
2092     return;
2093   wh = get_write_handle_in_dir (sr->h,
2094                                 (sr->sc->psearch_result == NULL)
2095                                 ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2096                                 : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2097                                 sr->sc->serialization,
2098                                 sr->serialization);
2099   if (NULL == wh)
2100   {
2101     GNUNET_break (0);
2102     goto cleanup;
2103   }
2104   uris = GNUNET_FS_uri_to_string (sr->uri);
2105   if ((GNUNET_OK != GNUNET_BIO_write_string (wh, uris)) ||
2106       (GNUNET_OK != GNUNET_BIO_write_string (wh,
2107                                              (sr->download != NULL)
2108                                              ? sr->download->serialization
2109                                              : NULL)) ||
2110       (GNUNET_OK !=
2111        GNUNET_BIO_write_string (wh,
2112                                 (sr->update_search != NULL)
2113                                 ? sr->update_search->serialization
2114                                 : NULL)) ||
2115       (GNUNET_OK != GNUNET_BIO_write_meta_data (wh, sr->meta)) ||
2116       (GNUNET_OK !=
2117        GNUNET_BIO_write (wh, &sr->key, sizeof(struct GNUNET_HashCode))) ||
2118       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, sr->mandatory_missing)) ||
2119       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, sr->optional_support)) ||
2120       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, sr->availability_success)) ||
2121       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, sr->availability_trials)))
2122   {
2123     GNUNET_break (0);
2124     goto cleanup;
2125   }
2126   if ((NULL != sr->uri) && (GNUNET_FS_URI_KSK == sr->sc->uri->type) &&
2127       (GNUNET_OK !=
2128        GNUNET_BIO_write (wh,
2129                          sr->keyword_bitmap,
2130                          (sr->sc->uri->data.ksk.keywordCount + 7) / 8)))
2131   {
2132     GNUNET_break (0);
2133     goto cleanup;
2134   }
2135   if (GNUNET_OK != GNUNET_BIO_write_close (wh))
2136   {
2137     wh = NULL;
2138     GNUNET_break (0);
2139     goto cleanup;
2140   }
2141   GNUNET_free_non_null (uris);
2142   return;
2143 cleanup:
2144   GNUNET_free_non_null (uris);
2145   if (NULL != wh)
2146     (void) GNUNET_BIO_write_close (wh);
2147   remove_sync_file_in_dir (sr->h,
2148                            (NULL == sr->sc->psearch_result)
2149                            ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2150                            : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2151                            sr->sc->serialization,
2152                            sr->serialization);
2153   GNUNET_free (sr->serialization);
2154   sr->serialization = NULL;
2155 }
2156
2157
2158 /**
2159  * Synchronize this search struct with its mirror
2160  * on disk.  Note that all internal FS-operations that change
2161  * publishing structs should already call "sync" internally,
2162  * so this function is likely not useful for clients.
2163  *
2164  * @param sc the struct to sync
2165  */
2166 void
2167 GNUNET_FS_search_sync_ (struct GNUNET_FS_SearchContext *sc)
2168 {
2169   struct GNUNET_BIO_WriteHandle *wh;
2170   char *uris;
2171   char in_pause;
2172   const char *category;
2173
2174   category = (NULL == sc->psearch_result) ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2175              : GNUNET_FS_SYNC_PATH_CHILD_SEARCH;
2176   if (NULL == sc->serialization)
2177     sc->serialization = make_serialization_file_name (sc->h, category);
2178   if (NULL == sc->serialization)
2179     return;
2180   uris = NULL;
2181   wh = get_write_handle (sc->h, category, sc->serialization);
2182   if (NULL == wh)
2183   {
2184     GNUNET_break (0);
2185     goto cleanup;
2186   }
2187   GNUNET_assert ((GNUNET_YES == GNUNET_FS_uri_test_ksk (sc->uri)) ||
2188                  (GNUNET_YES == GNUNET_FS_uri_test_sks (sc->uri)));
2189   uris = GNUNET_FS_uri_to_string (sc->uri);
2190   in_pause = (sc->task != NULL) ? 'r' : '\0';
2191   if ((GNUNET_OK != GNUNET_BIO_write_string (wh, uris)) ||
2192       (GNUNET_OK != write_start_time (wh, sc->start_time)) ||
2193       (GNUNET_OK != GNUNET_BIO_write_string (wh, sc->emsg)) ||
2194       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, (uint32_t) sc->options)) ||
2195       (GNUNET_OK != GNUNET_BIO_write (wh, &in_pause, sizeof(in_pause))) ||
2196       (GNUNET_OK != GNUNET_BIO_write_int32 (wh, sc->anonymity)))
2197   {
2198     GNUNET_break (0);
2199     goto cleanup;
2200   }
2201   GNUNET_free (uris);
2202   uris = NULL;
2203   if (GNUNET_OK != GNUNET_BIO_write_close (wh))
2204   {
2205     wh = NULL;
2206     GNUNET_break (0);
2207     goto cleanup;
2208   }
2209   return;
2210 cleanup:
2211   if (NULL != wh)
2212     (void) GNUNET_BIO_write_close (wh);
2213   GNUNET_free_non_null (uris);
2214   GNUNET_FS_remove_sync_file_ (sc->h, category, sc->serialization);
2215   GNUNET_free (sc->serialization);
2216   sc->serialization = NULL;
2217 }
2218
2219
2220 /**
2221  * Function called with a filename of serialized unindexing operation
2222  * to deserialize.
2223  *
2224  * @param cls the `struct GNUNET_FS_Handle *`
2225  * @param filename complete filename (absolute path)
2226  * @return #GNUNET_OK (continue to iterate)
2227  */
2228 static int
2229 deserialize_unindex_file (void *cls, const char *filename)
2230 {
2231   struct GNUNET_FS_Handle *h = cls;
2232   struct GNUNET_BIO_ReadHandle *rh;
2233   struct GNUNET_FS_UnindexContext *uc;
2234   struct GNUNET_FS_ProgressInfo pi;
2235   char *emsg;
2236   char *uris;
2237   uint32_t state;
2238
2239   uc = GNUNET_new (struct GNUNET_FS_UnindexContext);
2240   uc->h = h;
2241   uc->serialization = get_serialization_short_name (filename);
2242   rh = GNUNET_BIO_read_open (filename);
2243   if (NULL == rh)
2244   {
2245     GNUNET_break (0);
2246     goto cleanup;
2247   }
2248   uris = NULL;
2249   if ((GNUNET_OK !=
2250        GNUNET_BIO_read_string (rh, "unindex-fn", &uc->filename, 10 * 1024)) ||
2251       (GNUNET_OK != GNUNET_BIO_read_int64 (rh, &uc->file_size)) ||
2252       (GNUNET_OK != read_start_time (rh, &uc->start_time)) ||
2253       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &state)) ||
2254       (GNUNET_OK !=
2255        GNUNET_BIO_read (rh, "uri", &uc->chk, sizeof(struct ContentHashKey))) ||
2256       (GNUNET_OK !=
2257        GNUNET_BIO_read_string (rh, "unindex-kskuri", &uris, 10 * 1024)) ||
2258       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &uc->ksk_offset)))
2259   {
2260     GNUNET_free_non_null (uris);
2261     GNUNET_break (0);
2262     goto cleanup;
2263   }
2264   if (NULL != uris)
2265   {
2266     uc->ksk_uri = GNUNET_FS_uri_parse (uris, &emsg);
2267     GNUNET_free (uris);
2268     if (NULL == uc->ksk_uri)
2269     {
2270       GNUNET_break (0);
2271       GNUNET_free_non_null (emsg);
2272       goto cleanup;
2273     }
2274   }
2275   if ((uc->ksk_offset > 0) &&
2276       ((NULL == uc->ksk_uri) ||
2277        (uc->ksk_offset > uc->ksk_uri->data.ksk.keywordCount)))
2278   {
2279     GNUNET_break (0);
2280     goto cleanup;
2281   }
2282   uc->state = (enum UnindexState) state;
2283   switch (state)
2284   {
2285   case UNINDEX_STATE_HASHING:
2286     break;
2287
2288   case UNINDEX_STATE_FS_NOTIFY:
2289     if (GNUNET_OK != GNUNET_BIO_read (rh,
2290                                       "unindex-hash",
2291                                       &uc->file_id,
2292                                       sizeof(struct GNUNET_HashCode)))
2293     {
2294       GNUNET_break (0);
2295       goto cleanup;
2296     }
2297     break;
2298
2299   case UNINDEX_STATE_DS_REMOVE:
2300   case UNINDEX_STATE_EXTRACT_KEYWORDS:
2301   case UNINDEX_STATE_DS_REMOVE_KBLOCKS:
2302     break;
2303
2304   case UNINDEX_STATE_COMPLETE:
2305     break;
2306
2307   case UNINDEX_STATE_ERROR:
2308     if (GNUNET_OK !=
2309         GNUNET_BIO_read_string (rh, "unindex-emsg", &uc->emsg, 10 * 1024))
2310     {
2311       GNUNET_break (0);
2312       goto cleanup;
2313     }
2314     break;
2315
2316   default:
2317     GNUNET_break (0);
2318     goto cleanup;
2319   }
2320   uc->top = GNUNET_FS_make_top (h, &GNUNET_FS_unindex_signal_suspend_, uc);
2321   pi.status = GNUNET_FS_STATUS_UNINDEX_RESUME;
2322   pi.value.unindex.specifics.resume.message = uc->emsg;
2323   GNUNET_FS_unindex_make_status_ (&pi,
2324                                   uc,
2325                                   (uc->state == UNINDEX_STATE_COMPLETE)
2326                                   ? uc->file_size
2327                                   : 0);
2328   switch (uc->state)
2329   {
2330   case UNINDEX_STATE_HASHING:
2331     uc->fhc = GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE,
2332                                        uc->filename,
2333                                        HASHING_BLOCKSIZE,
2334                                        &GNUNET_FS_unindex_process_hash_,
2335                                        uc);
2336     break;
2337
2338   case UNINDEX_STATE_FS_NOTIFY:
2339     uc->state = UNINDEX_STATE_HASHING;
2340     GNUNET_FS_unindex_process_hash_ (uc, &uc->file_id);
2341     break;
2342
2343   case UNINDEX_STATE_DS_REMOVE:
2344     GNUNET_FS_unindex_do_remove_ (uc);
2345     break;
2346
2347   case UNINDEX_STATE_EXTRACT_KEYWORDS:
2348     GNUNET_FS_unindex_do_extract_keywords_ (uc);
2349     break;
2350
2351   case UNINDEX_STATE_DS_REMOVE_KBLOCKS:
2352     GNUNET_FS_unindex_do_remove_kblocks_ (uc);
2353     break;
2354
2355   case UNINDEX_STATE_COMPLETE:
2356   case UNINDEX_STATE_ERROR:
2357     /* no need to resume any operation, we were done */
2358     break;
2359
2360   default:
2361     break;
2362   }
2363   if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
2364   {
2365     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2366                 _ ("Failure while resuming unindexing operation `%s': %s\n"),
2367                 filename,
2368                 emsg);
2369     GNUNET_free (emsg);
2370   }
2371   return GNUNET_OK;
2372 cleanup:
2373   GNUNET_free_non_null (uc->filename);
2374   if ((NULL != rh) && (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)))
2375   {
2376     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2377                 _ ("Failed to resume unindexing operation `%s': %s\n"),
2378                 filename,
2379                 emsg);
2380     GNUNET_free (emsg);
2381   }
2382   if (NULL != uc->serialization)
2383     GNUNET_FS_remove_sync_file_ (h,
2384                                  GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
2385                                  uc->serialization);
2386   GNUNET_free_non_null (uc->serialization);
2387   GNUNET_free (uc);
2388   return GNUNET_OK;
2389 }
2390
2391
2392 /**
2393  * Deserialize a download.
2394  *
2395  * @param h overall context
2396  * @param rh file to deserialize from
2397  * @param parent parent download
2398  * @param search associated search
2399  * @param serialization name under which the search was serialized
2400  */
2401 static void
2402 deserialize_download (struct GNUNET_FS_Handle *h,
2403                       struct GNUNET_BIO_ReadHandle *rh,
2404                       struct GNUNET_FS_DownloadContext *parent,
2405                       struct GNUNET_FS_SearchResult *search,
2406                       const char *serialization);
2407
2408
2409 /**
2410  * Deserialize a search.
2411  *
2412  * @param h overall context
2413  * @param rh file to deserialize from
2414  * @param psearch_result parent search result
2415  * @param serialization name under which the search was serialized
2416  */
2417 static struct GNUNET_FS_SearchContext *
2418 deserialize_search (struct GNUNET_FS_Handle *h,
2419                     struct GNUNET_BIO_ReadHandle *rh,
2420                     struct GNUNET_FS_SearchResult *psearch_result,
2421                     const char *serialization);
2422
2423
2424 /**
2425  * Function called with a filename of serialized search result
2426  * to deserialize.
2427  *
2428  * @param cls the `struct GNUNET_FS_SearchContext *`
2429  * @param filename complete filename (absolute path)
2430  * @return #GNUNET_OK (continue to iterate)
2431  */
2432 static int
2433 deserialize_search_result (void *cls, const char *filename)
2434 {
2435   struct GNUNET_FS_SearchContext *sc = cls;
2436   char *ser;
2437   char *uris;
2438   char *emsg;
2439   char *download;
2440   char *update_srch;
2441   struct GNUNET_BIO_ReadHandle *rh;
2442   struct GNUNET_BIO_ReadHandle *drh;
2443   struct GNUNET_FS_SearchResult *sr;
2444
2445   ser = get_serialization_short_name (filename);
2446   rh = GNUNET_BIO_read_open (filename);
2447   if (NULL == rh)
2448   {
2449     if (NULL != ser)
2450     {
2451       remove_sync_file_in_dir (sc->h,
2452                                (NULL == sc->psearch_result)
2453                                ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2454                                : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2455                                sc->serialization,
2456                                ser);
2457       GNUNET_free (ser);
2458     }
2459     return GNUNET_OK;
2460   }
2461   emsg = NULL;
2462   uris = NULL;
2463   download = NULL;
2464   update_srch = NULL;
2465   sr = GNUNET_new (struct GNUNET_FS_SearchResult);
2466   sr->h = sc->h;
2467   sr->sc = sc;
2468   sr->serialization = ser;
2469   if ((GNUNET_OK !=
2470        GNUNET_BIO_read_string (rh, "result-uri", &uris, 10 * 1024)) ||
2471       (NULL == (sr->uri = GNUNET_FS_uri_parse (uris, &emsg))) ||
2472       (GNUNET_OK !=
2473        GNUNET_BIO_read_string (rh, "download-lnk", &download, 16)) ||
2474       (GNUNET_OK !=
2475        GNUNET_BIO_read_string (rh, "search-lnk", &update_srch, 16)) ||
2476       (GNUNET_OK != GNUNET_BIO_read_meta_data (rh, "result-meta", &sr->meta)) ||
2477       (GNUNET_OK != GNUNET_BIO_read (rh,
2478                                      "result-key",
2479                                      &sr->key,
2480                                      sizeof(struct GNUNET_HashCode))) ||
2481       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &sr->mandatory_missing)) ||
2482       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &sr->optional_support)) ||
2483       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &sr->availability_success)) ||
2484       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &sr->availability_trials)))
2485   {
2486     GNUNET_break (0);
2487     goto cleanup;
2488   }
2489   if (GNUNET_FS_URI_KSK == sr->sc->uri->type)
2490   {
2491     sr->keyword_bitmap = GNUNET_malloc (
2492       (sr->sc->uri->data.ksk.keywordCount + 7) / 8);   /* round up, count bits */
2493     if (GNUNET_OK !=
2494         GNUNET_BIO_read (rh,
2495                          "keyword-bitmap",
2496                          sr->keyword_bitmap,
2497                          (sr->sc->uri->data.ksk.keywordCount + 7) / 8))
2498     {
2499       GNUNET_break (0);
2500       goto cleanup;
2501     }
2502   }
2503   GNUNET_free (uris);
2504   if (NULL != download)
2505   {
2506     drh = get_read_handle (sc->h, GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD, download);
2507     if (NULL != drh)
2508     {
2509       deserialize_download (sc->h, drh, NULL, sr, download);
2510       if (GNUNET_OK != GNUNET_BIO_read_close (drh, &emsg))
2511       {
2512         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2513                     _ ("Failed to resume sub-download `%s': %s\n"),
2514                     download,
2515                     emsg);
2516         GNUNET_free (emsg);
2517       }
2518     }
2519     GNUNET_free (download);
2520   }
2521   if (NULL != update_srch)
2522   {
2523     drh =
2524       get_read_handle (sc->h, GNUNET_FS_SYNC_PATH_CHILD_SEARCH, update_srch);
2525     if (NULL != drh)
2526     {
2527       deserialize_search (sc->h, drh, sr, update_srch);
2528       if (GNUNET_OK != GNUNET_BIO_read_close (drh, &emsg))
2529       {
2530         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2531                     _ ("Failed to resume sub-search `%s': %s\n"),
2532                     update_srch,
2533                     emsg);
2534         GNUNET_free (emsg);
2535       }
2536     }
2537     GNUNET_free (update_srch);
2538   }
2539   GNUNET_break (GNUNET_YES == GNUNET_CONTAINER_multihashmap_put (
2540                   sc->master_result_map,
2541                   &sr->key,
2542                   sr,
2543                   GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
2544   if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
2545   {
2546     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2547                 _ ("Failure while resuming search operation `%s': %s\n"),
2548                 filename,
2549                 emsg);
2550     GNUNET_free (emsg);
2551   }
2552   return GNUNET_OK;
2553 cleanup:
2554   GNUNET_free_non_null (download);
2555   GNUNET_free_non_null (emsg);
2556   GNUNET_free_non_null (uris);
2557   GNUNET_free_non_null (update_srch);
2558   if (NULL != sr->uri)
2559     GNUNET_FS_uri_destroy (sr->uri);
2560   if (NULL != sr->meta)
2561     GNUNET_CONTAINER_meta_data_destroy (sr->meta);
2562   GNUNET_free (sr->serialization);
2563   GNUNET_free (sr);
2564   if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
2565   {
2566     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2567                 _ ("Failure while resuming search operation `%s': %s\n"),
2568                 filename,
2569                 emsg);
2570     GNUNET_free (emsg);
2571   }
2572   return GNUNET_OK;
2573 }
2574
2575
2576 /**
2577  * Send the 'resume' signal to the callback; also actually
2578  * resume the download (put it in the queue).  Does this
2579  * recursively for the top-level download and all child
2580  * downloads.
2581  *
2582  * @param dc download to resume
2583  */
2584 static void
2585 signal_download_resume (struct GNUNET_FS_DownloadContext *dc)
2586 {
2587   struct GNUNET_FS_DownloadContext *dcc;
2588   struct GNUNET_FS_ProgressInfo pi;
2589
2590   pi.status = GNUNET_FS_STATUS_DOWNLOAD_RESUME;
2591   pi.value.download.specifics.resume.meta = dc->meta;
2592   pi.value.download.specifics.resume.message = dc->emsg;
2593   GNUNET_FS_download_make_status_ (&pi, dc);
2594   dcc = dc->child_head;
2595   while (NULL != dcc)
2596   {
2597     signal_download_resume (dcc);
2598     dcc = dcc->next;
2599   }
2600 }
2601
2602
2603 /**
2604  * Signal resuming of a search to our clients (for the
2605  * top level search and all sub-searches).
2606  *
2607  * @param sc search being resumed
2608  */
2609 static void
2610 signal_search_resume (struct GNUNET_FS_SearchContext *sc);
2611
2612
2613 /**
2614  * Iterator over search results signaling resume to the client for
2615  * each result.
2616  *
2617  * @param cls closure, the `struct GNUNET_FS_SearchContext *`
2618  * @param key current key code
2619  * @param value value in the hash map, the `struct GNUNET_FS_SearchResult *`
2620  * @return #GNUNET_YES (we should continue to iterate)
2621  */
2622 static int
2623 signal_result_resume (void *cls, const struct GNUNET_HashCode *key, void *value)
2624 {
2625   struct GNUNET_FS_SearchContext *sc = cls;
2626   struct GNUNET_FS_ProgressInfo pi;
2627   struct GNUNET_FS_SearchResult *sr = value;
2628
2629   if (0 == sr->mandatory_missing)
2630   {
2631     pi.status = GNUNET_FS_STATUS_SEARCH_RESUME_RESULT;
2632     pi.value.search.specifics.resume_result.meta = sr->meta;
2633     pi.value.search.specifics.resume_result.uri = sr->uri;
2634     pi.value.search.specifics.resume_result.result = sr;
2635     pi.value.search.specifics.resume_result.availability_rank =
2636       2 * sr->availability_success - sr->availability_trials;
2637     pi.value.search.specifics.resume_result.availability_certainty =
2638       sr->availability_trials;
2639     pi.value.search.specifics.resume_result.applicability_rank =
2640       sr->optional_support;
2641     sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
2642   }
2643   if (NULL != sr->download)
2644   {
2645     signal_download_resume (sr->download);
2646   }
2647   else
2648   {
2649     GNUNET_FS_search_start_probe_ (sr);
2650   }
2651   if (NULL != sr->update_search)
2652     signal_search_resume (sr->update_search);
2653   return GNUNET_YES;
2654 }
2655
2656
2657 /**
2658  * Free memory allocated by the search context and its children
2659  *
2660  * @param sc search context to free
2661  */
2662 static void
2663 free_search_context (struct GNUNET_FS_SearchContext *sc);
2664
2665
2666 /**
2667  * Iterator over search results freeing each.
2668  *
2669  * @param cls closure, the `struct GNUNET_FS_SearchContext *`
2670  * @param key current key code
2671  * @param value value in the hash map, the `struct GNUNET_FS_SearchResult *`
2672  * @return #GNUNET_YES (we should continue to iterate)
2673  */
2674 static int
2675 free_result (void *cls, const struct GNUNET_HashCode *key, void *value)
2676 {
2677   struct GNUNET_FS_SearchResult *sr = value;
2678
2679   if (NULL != sr->update_search)
2680   {
2681     free_search_context (sr->update_search);
2682     GNUNET_assert (NULL == sr->update_search);
2683   }
2684   GNUNET_CONTAINER_meta_data_destroy (sr->meta);
2685   GNUNET_FS_uri_destroy (sr->uri);
2686   GNUNET_free (sr);
2687   return GNUNET_YES;
2688 }
2689
2690
2691 /**
2692  * Free memory allocated by the search context and its children
2693  *
2694  * @param sc search context to free
2695  */
2696 static void
2697 free_search_context (struct GNUNET_FS_SearchContext *sc)
2698 {
2699   if (NULL != sc->serialization)
2700   {
2701     GNUNET_FS_remove_sync_file_ (sc->h,
2702                                  (sc->psearch_result == NULL)
2703                                  ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2704                                  : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2705                                  sc->serialization);
2706     GNUNET_FS_remove_sync_dir_ (sc->h,
2707                                 (sc->psearch_result == NULL)
2708                                 ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2709                                 : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2710                                 sc->serialization);
2711   }
2712   GNUNET_free_non_null (sc->serialization);
2713   GNUNET_free_non_null (sc->emsg);
2714   if (NULL != sc->uri)
2715     GNUNET_FS_uri_destroy (sc->uri);
2716   if (NULL != sc->master_result_map)
2717   {
2718     GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
2719                                            &free_result,
2720                                            sc);
2721     GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
2722   }
2723   GNUNET_free (sc);
2724 }
2725
2726
2727 /**
2728  * Function called with a filename of serialized sub-download
2729  * to deserialize.
2730  *
2731  * @param cls the `struct GNUNET_FS_DownloadContext *` (parent)
2732  * @param filename complete filename (absolute path)
2733  * @return #GNUNET_OK (continue to iterate)
2734  */
2735 static int
2736 deserialize_subdownload (void *cls, const char *filename)
2737 {
2738   struct GNUNET_FS_DownloadContext *parent = cls;
2739   char *ser;
2740   char *emsg;
2741   struct GNUNET_BIO_ReadHandle *rh;
2742
2743   ser = get_serialization_short_name (filename);
2744   rh = GNUNET_BIO_read_open (filename);
2745   if (NULL == rh)
2746   {
2747     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2748                 _ (
2749                   "Failed to resume sub-download `%s': could not open file `%s'\n"),
2750                 ser,
2751                 filename);
2752     GNUNET_free (ser);
2753     return GNUNET_OK;
2754   }
2755   deserialize_download (parent->h, rh, parent, NULL, ser);
2756   if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
2757   {
2758     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2759                 _ ("Failed to resume sub-download `%s': %s\n"),
2760                 ser,
2761                 emsg);
2762     GNUNET_free (emsg);
2763   }
2764   GNUNET_free (ser);
2765   return GNUNET_OK;
2766 }
2767
2768
2769 /**
2770  * Free this download context and all of its descendants.
2771  * (only works during deserialization since not all possible
2772  * state it taken care of).
2773  *
2774  * @param dc context to free
2775  */
2776 static void
2777 free_download_context (struct GNUNET_FS_DownloadContext *dc)
2778 {
2779   struct GNUNET_FS_DownloadContext *dcc;
2780
2781   if (NULL != dc->meta)
2782     GNUNET_CONTAINER_meta_data_destroy (dc->meta);
2783   if (NULL != dc->uri)
2784     GNUNET_FS_uri_destroy (dc->uri);
2785   GNUNET_free_non_null (dc->temp_filename);
2786   GNUNET_free_non_null (dc->emsg);
2787   GNUNET_free_non_null (dc->filename);
2788   GNUNET_free_non_null (dc->serialization);
2789   while (NULL != (dcc = dc->child_head))
2790   {
2791     GNUNET_CONTAINER_DLL_remove (dc->child_head, dc->child_tail, dcc);
2792     free_download_context (dcc);
2793   }
2794   GNUNET_FS_free_download_request_ (dc->top_request);
2795   if (NULL != dc->active)
2796     GNUNET_CONTAINER_multihashmap_destroy (dc->active);
2797   GNUNET_free (dc);
2798 }
2799
2800
2801 /**
2802  * Deserialize a download.
2803  *
2804  * @param h overall context
2805  * @param rh file to deserialize from
2806  * @param parent parent download
2807  * @param search associated search
2808  * @param serialization name under which the search was serialized
2809  */
2810 static void
2811 deserialize_download (struct GNUNET_FS_Handle *h,
2812                       struct GNUNET_BIO_ReadHandle *rh,
2813                       struct GNUNET_FS_DownloadContext *parent,
2814                       struct GNUNET_FS_SearchResult *search,
2815                       const char *serialization)
2816 {
2817   struct GNUNET_FS_DownloadContext *dc;
2818   char *emsg;
2819   char *uris;
2820   char *dn;
2821   uint32_t options;
2822   uint32_t status;
2823
2824   uris = NULL;
2825   emsg = NULL;
2826   dc = GNUNET_new (struct GNUNET_FS_DownloadContext);
2827   dc->parent = parent;
2828   dc->h = h;
2829   dc->serialization = GNUNET_strdup (serialization);
2830   if ((GNUNET_OK !=
2831        GNUNET_BIO_read_string (rh, "download-uri", &uris, 10 * 1024)) ||
2832       (NULL == (dc->uri = GNUNET_FS_uri_parse (uris, &emsg))) ||
2833       ((GNUNET_YES != GNUNET_FS_uri_test_chk (dc->uri)) &&
2834        (GNUNET_YES != GNUNET_FS_uri_test_loc (dc->uri))) ||
2835       (GNUNET_OK !=
2836        GNUNET_BIO_read_meta_data (rh, "download-meta", &dc->meta)) ||
2837       (GNUNET_OK !=
2838        GNUNET_BIO_read_string (rh, "download-emsg", &dc->emsg, 10 * 1024)) ||
2839       (GNUNET_OK !=
2840        GNUNET_BIO_read_string (rh, "download-fn", &dc->filename, 10 * 1024)) ||
2841       (GNUNET_OK != GNUNET_BIO_read_string (rh,
2842                                             "download-tfn",
2843                                             &dc->temp_filename,
2844                                             10 * 1024)) ||
2845       (GNUNET_OK != GNUNET_BIO_read_int64 (rh, &dc->old_file_size)) ||
2846       (GNUNET_OK != GNUNET_BIO_read_int64 (rh, &dc->offset)) ||
2847       (GNUNET_OK != GNUNET_BIO_read_int64 (rh, &dc->length)) ||
2848       (GNUNET_OK != GNUNET_BIO_read_int64 (rh, &dc->completed)) ||
2849       (GNUNET_OK != read_start_time (rh, &dc->start_time)) ||
2850       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &dc->anonymity)) ||
2851       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &options)) ||
2852       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &status)))
2853   {
2854     GNUNET_break (0);
2855     goto cleanup;
2856   }
2857   dc->options = (enum GNUNET_FS_DownloadOptions) options;
2858   dc->active =
2859     GNUNET_CONTAINER_multihashmap_create (1 + 2 * (dc->length / DBLOCK_SIZE),
2860                                           GNUNET_NO);
2861   dc->has_finished = (int) status;
2862   dc->treedepth =
2863     GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size (dc->uri));
2864   if (GNUNET_FS_uri_test_loc (dc->uri))
2865     GNUNET_assert (GNUNET_OK ==
2866                    GNUNET_FS_uri_loc_get_peer_identity (dc->uri, &dc->target));
2867   if (NULL == dc->emsg)
2868   {
2869     dc->top_request = read_download_request (rh);
2870     if (NULL == dc->top_request)
2871     {
2872       GNUNET_break (0);
2873       goto cleanup;
2874     }
2875   }
2876   dn = get_download_sync_filename (dc, dc->serialization, ".dir");
2877   if (NULL != dn)
2878   {
2879     if (GNUNET_YES == GNUNET_DISK_directory_test (dn, GNUNET_YES))
2880       GNUNET_DISK_directory_scan (dn, &deserialize_subdownload, dc);
2881     GNUNET_free (dn);
2882   }
2883   if (NULL != parent)
2884   {
2885     GNUNET_CONTAINER_DLL_insert (parent->child_head, parent->child_tail, dc);
2886   }
2887   if (NULL != search)
2888   {
2889     dc->search = search;
2890     search->download = dc;
2891   }
2892   if ((NULL == parent) && (NULL == search))
2893   {
2894     dc->top =
2895       GNUNET_FS_make_top (dc->h, &GNUNET_FS_download_signal_suspend_, dc);
2896     signal_download_resume (dc);
2897   }
2898   GNUNET_free (uris);
2899   GNUNET_assert (NULL == dc->job_queue);
2900   dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc);
2901   return;
2902 cleanup:
2903   GNUNET_free_non_null (uris);
2904   GNUNET_free_non_null (emsg);
2905   free_download_context (dc);
2906 }
2907
2908
2909 /**
2910  * Signal resuming of a search to our clients (for the
2911  * top level search and all sub-searches).
2912  *
2913  * @param sc search being resumed
2914  */
2915 static void
2916 signal_search_resume (struct GNUNET_FS_SearchContext *sc)
2917 {
2918   struct GNUNET_FS_ProgressInfo pi;
2919
2920   pi.status = GNUNET_FS_STATUS_SEARCH_RESUME;
2921   pi.value.search.specifics.resume.message = sc->emsg;
2922   pi.value.search.specifics.resume.is_paused =
2923     (NULL == sc->mq) ? GNUNET_YES : GNUNET_NO;
2924   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
2925   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
2926                                          &signal_result_resume,
2927                                          sc);
2928 }
2929
2930
2931 /**
2932  * Deserialize a search.
2933  *
2934  * @param h overall context
2935  * @param rh file to deserialize from
2936  * @param psearch_result parent search result
2937  * @param serialization name under which the search was serialized
2938  */
2939 static struct GNUNET_FS_SearchContext *
2940 deserialize_search (struct GNUNET_FS_Handle *h,
2941                     struct GNUNET_BIO_ReadHandle *rh,
2942                     struct GNUNET_FS_SearchResult *psearch_result,
2943                     const char *serialization)
2944 {
2945   struct GNUNET_FS_SearchContext *sc;
2946   char *emsg;
2947   char *uris;
2948   char *dn;
2949   uint32_t options;
2950   char in_pause;
2951
2952   if ((NULL != psearch_result) && (NULL != psearch_result->update_search))
2953   {
2954     GNUNET_break (0);
2955     return NULL;
2956   }
2957   uris = NULL;
2958   emsg = NULL;
2959   sc = GNUNET_new (struct GNUNET_FS_SearchContext);
2960   if (NULL != psearch_result)
2961   {
2962     sc->psearch_result = psearch_result;
2963     psearch_result->update_search = sc;
2964   }
2965   sc->h = h;
2966   sc->serialization = GNUNET_strdup (serialization);
2967   if ((GNUNET_OK !=
2968        GNUNET_BIO_read_string (rh, "search-uri", &uris, 10 * 1024)) ||
2969       (NULL == (sc->uri = GNUNET_FS_uri_parse (uris, &emsg))) ||
2970       ((GNUNET_YES != GNUNET_FS_uri_test_ksk (sc->uri)) &&
2971        (GNUNET_YES != GNUNET_FS_uri_test_sks (sc->uri))) ||
2972       (GNUNET_OK != read_start_time (rh, &sc->start_time)) ||
2973       (GNUNET_OK !=
2974        GNUNET_BIO_read_string (rh, "search-emsg", &sc->emsg, 10 * 1024)) ||
2975       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &options)) ||
2976       (GNUNET_OK !=
2977        GNUNET_BIO_read (rh, "search-pause", &in_pause, sizeof(in_pause))) ||
2978       (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &sc->anonymity)))
2979   {
2980     GNUNET_break (0);
2981     goto cleanup;
2982   }
2983   sc->options = (enum GNUNET_FS_SearchOptions) options;
2984   sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
2985   dn = get_serialization_file_name_in_dir (h,
2986                                            (NULL == sc->psearch_result)
2987                                            ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2988                                            : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2989                                            sc->serialization,
2990                                            "");
2991   if (NULL != dn)
2992   {
2993     if (GNUNET_YES == GNUNET_DISK_directory_test (dn, GNUNET_YES))
2994       GNUNET_DISK_directory_scan (dn, &deserialize_search_result, sc);
2995     GNUNET_free (dn);
2996   }
2997   if (('\0' == in_pause) &&
2998       (GNUNET_OK != GNUNET_FS_search_start_searching_ (sc)))
2999   {
3000     GNUNET_log (
3001       GNUNET_ERROR_TYPE_WARNING,
3002       _ ("Could not resume running search, will resume as paused search\n"));
3003   }
3004   signal_search_resume (sc);
3005   GNUNET_free (uris);
3006   return sc;
3007 cleanup:
3008   GNUNET_free_non_null (emsg);
3009   free_search_context (sc);
3010   GNUNET_free_non_null (uris);
3011   return NULL;
3012 }
3013
3014
3015 /**
3016  * Function called with a filename of serialized search operation
3017  * to deserialize.
3018  *
3019  * @param cls the `struct GNUNET_FS_Handle *`
3020  * @param filename complete filename (absolute path)
3021  * @return #GNUNET_OK (continue to iterate)
3022  */
3023 static int
3024 deserialize_search_file (void *cls, const char *filename)
3025 {
3026   struct GNUNET_FS_Handle *h = cls;
3027   char *ser;
3028   char *emsg;
3029   struct GNUNET_BIO_ReadHandle *rh;
3030   struct GNUNET_FS_SearchContext *sc;
3031   struct stat buf;
3032
3033   if (0 != stat (filename, &buf))
3034   {
3035     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", filename);
3036     return GNUNET_OK;
3037   }
3038   if (S_ISDIR (buf.st_mode))
3039     return GNUNET_OK; /* skip directories */
3040   ser = get_serialization_short_name (filename);
3041   rh = GNUNET_BIO_read_open (filename);
3042   if (NULL == rh)
3043   {
3044     if (NULL != ser)
3045     {
3046       GNUNET_FS_remove_sync_file_ (h, GNUNET_FS_SYNC_PATH_MASTER_SEARCH, ser);
3047       GNUNET_free (ser);
3048     }
3049     return GNUNET_OK;
3050   }
3051   sc = deserialize_search (h, rh, NULL, ser);
3052   if (NULL != sc)
3053     sc->top = GNUNET_FS_make_top (h, &GNUNET_FS_search_signal_suspend_, sc);
3054   GNUNET_free (ser);
3055   if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
3056   {
3057     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3058                 _ ("Failure while resuming search operation `%s': %s\n"),
3059                 filename,
3060                 emsg);
3061     GNUNET_free (emsg);
3062   }
3063   return GNUNET_OK;
3064 }
3065
3066
3067 /**
3068  * Function called with a filename of serialized download operation
3069  * to deserialize.
3070  *
3071  * @param cls the `struct GNUNET_FS_Handle *`
3072  * @param filename complete filename (absolute path)
3073  * @return #GNUNET_OK (continue to iterate)
3074  */
3075 static int
3076 deserialize_download_file (void *cls, const char *filename)
3077 {
3078   struct GNUNET_FS_Handle *h = cls;
3079   char *ser;
3080   char *emsg;
3081   struct GNUNET_BIO_ReadHandle *rh;
3082
3083   ser = get_serialization_short_name (filename);
3084   rh = GNUNET_BIO_read_open (filename);
3085   if (NULL == rh)
3086   {
3087     if (0 != unlink (filename))
3088       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", filename);
3089     GNUNET_free (ser);
3090     return GNUNET_OK;
3091   }
3092   deserialize_download (h, rh, NULL, NULL, ser);
3093   GNUNET_free (ser);
3094   if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
3095   {
3096     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3097                 _ ("Failure while resuming download operation `%s': %s\n"),
3098                 filename,
3099                 emsg);
3100     GNUNET_free (emsg);
3101   }
3102   return GNUNET_OK;
3103 }
3104
3105
3106 /**
3107  * Deserialize informatin about pending operations.
3108  *
3109  * @param master_path which master directory should be scanned
3110  * @param proc function to call for each entry (will get @a h for 'cls')
3111  * @param h the `struct GNUNET_FS_Handle *`
3112  */
3113 static void
3114 deserialization_master (const char *master_path,
3115                         GNUNET_FileNameCallback proc,
3116                         struct GNUNET_FS_Handle *h)
3117 {
3118   char *dn;
3119
3120   dn = get_serialization_file_name (h, master_path, "");
3121   if (NULL == dn)
3122     return;
3123   if (GNUNET_YES == GNUNET_DISK_directory_test (dn, GNUNET_YES))
3124     GNUNET_DISK_directory_scan (dn, proc, h);
3125   GNUNET_free (dn);
3126 }
3127
3128
3129 /**
3130  * Setup a connection to the file-sharing service.
3131  *
3132  * @param cfg configuration to use
3133  * @param client_name unique identifier for this client
3134  * @param upcb function to call to notify about FS actions
3135  * @param upcb_cls closure for @a upcb
3136  * @param flags specific attributes for fs-operations
3137  * @param ... list of optional options, terminated with #GNUNET_FS_OPTIONS_END
3138  * @return NULL on error
3139  */
3140 struct GNUNET_FS_Handle *
3141 GNUNET_FS_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
3142                  const char *client_name,
3143                  GNUNET_FS_ProgressCallback upcb,
3144                  void *upcb_cls,
3145                  enum GNUNET_FS_Flags flags,
3146                  ...)
3147 {
3148   struct GNUNET_FS_Handle *ret;
3149   enum GNUNET_FS_OPTIONS opt;
3150   va_list ap;
3151
3152   ret = GNUNET_new (struct GNUNET_FS_Handle);
3153   ret->cfg = cfg;
3154   ret->client_name = GNUNET_strdup (client_name);
3155   ret->upcb = upcb;
3156   ret->upcb_cls = upcb_cls;
3157   ret->flags = flags;
3158   ret->max_parallel_downloads = DEFAULT_MAX_PARALLEL_DOWNLOADS;
3159   ret->max_parallel_requests = DEFAULT_MAX_PARALLEL_REQUESTS;
3160   ret->avg_block_latency =
3161     GNUNET_TIME_UNIT_MINUTES; /* conservative starting point */
3162   va_start (ap, flags);
3163   while (GNUNET_FS_OPTIONS_END !=
3164          (opt = GNUNET_VA_ARG_ENUM (ap, GNUNET_FS_OPTIONS)))
3165   {
3166     switch (opt)
3167     {
3168     case GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM:
3169       ret->max_parallel_downloads = va_arg (ap, unsigned int);
3170
3171       break;
3172
3173     case GNUNET_FS_OPTIONS_REQUEST_PARALLELISM:
3174       ret->max_parallel_requests = va_arg (ap, unsigned int);
3175
3176       break;
3177
3178     default:
3179       GNUNET_break (0);
3180       GNUNET_free (ret->client_name);
3181       GNUNET_free (ret);
3182       va_end (ap);
3183       return NULL;
3184     }
3185   }
3186   va_end (ap);
3187   if (0 != (GNUNET_FS_FLAGS_PERSISTENCE & flags))
3188   {
3189     deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_PUBLISH,
3190                             &deserialize_publish_file,
3191                             ret);
3192     deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
3193                             &deserialize_search_file,
3194                             ret);
3195     deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
3196                             &deserialize_download_file,
3197                             ret);
3198     deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
3199                             &deserialize_unindex_file,
3200                             ret);
3201   }
3202   return ret;
3203 }
3204
3205
3206 /**
3207  * Close our connection with the file-sharing service.
3208  * The callback given to #GNUNET_FS_start() will no longer be
3209  * called after this function returns.
3210  * This function MUST NOT be called from within the
3211  * callback itself.
3212  *
3213  * @param h handle that was returned from #GNUNET_FS_start()
3214  */
3215 void
3216 GNUNET_FS_stop (struct GNUNET_FS_Handle *h)
3217 {
3218   while (NULL != h->top_head)
3219     h->top_head->ssf (h->top_head->ssf_cls);
3220   if (NULL != h->queue_job)
3221     GNUNET_SCHEDULER_cancel (h->queue_job);
3222   GNUNET_free (h->client_name);
3223   GNUNET_free (h);
3224 }
3225
3226
3227 /* end of fs_api.c */