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