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