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