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