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