fix
[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, 2011 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         read_start_time (rh, &ret->start_time)) ||
773        (GNUNET_OK !=
774         GNUNET_BIO_read_string (rh, "emsg", &ret->emsg, 16*1024)) ||
775        (GNUNET_OK !=
776         GNUNET_BIO_read_string (rh, "fn", &ret->filename, 16*1024)) ||
777        (GNUNET_OK !=
778         GNUNET_BIO_read_int64 (rh, &ret->bo.expiration_time.abs_value)) ||
779        (GNUNET_OK !=
780         GNUNET_BIO_read_int32 (rh, &ret->bo.anonymity_level)) ||
781        (GNUNET_OK !=
782         GNUNET_BIO_read_int32 (rh, &ret->bo.content_priority)) ||
783        (GNUNET_OK !=
784         GNUNET_BIO_read_int32 (rh, &ret->bo.replication_level)) )
785     {
786       GNUNET_break (0);      
787       goto cleanup;
788     }
789   switch (b)
790     {
791     case 0: /* file-insert */
792       if (GNUNET_OK !=
793           GNUNET_BIO_read_int64 (rh, &ret->data.file.file_size))
794         {
795           GNUNET_break (0);
796           goto cleanup;
797         }
798       ret->is_directory = GNUNET_NO;
799       ret->data.file.do_index = GNUNET_NO;
800       ret->data.file.have_hash = GNUNET_NO;
801       ret->data.file.index_start_confirmed = GNUNET_NO;
802       if (GNUNET_NO == ret->is_published) 
803         {
804           if (NULL == ret->filename)
805             {
806               ret->data.file.reader = &GNUNET_FS_data_reader_copy_;
807               ret->data.file.reader_cls = GNUNET_malloc_large (ret->data.file.file_size);
808               if (ret->data.file.reader_cls == NULL)
809                 goto cleanup;
810               if (GNUNET_OK !=
811                   GNUNET_BIO_read (rh, "file-data", ret->data.file.reader_cls, ret->data.file.file_size))
812                 {
813                   GNUNET_break (0);
814                   goto cleanup;
815                 }
816             }      
817           else
818             {
819               ret->data.file.reader = &GNUNET_FS_data_reader_file_;
820               ret->data.file.reader_cls = GNUNET_FS_make_file_reader_context_ (ret->filename);
821             }
822         }
823       break;
824     case 1: /* file-index, no hash */
825       if (NULL == ret->filename)
826         {
827           GNUNET_break (0);               
828           goto cleanup;
829         }
830       if (GNUNET_OK !=
831           GNUNET_BIO_read_int64 (rh, &ret->data.file.file_size))
832         {
833           GNUNET_break (0);
834           goto cleanup;
835         }
836       ret->is_directory = GNUNET_NO;
837       ret->data.file.do_index = GNUNET_YES;
838       ret->data.file.have_hash = GNUNET_NO;
839       ret->data.file.index_start_confirmed = GNUNET_NO;
840       ret->data.file.reader = &GNUNET_FS_data_reader_file_;
841       ret->data.file.reader_cls = GNUNET_FS_make_file_reader_context_ (ret->filename);
842       break;
843     case 2: /* file-index-with-hash */
844       if (NULL == ret->filename)
845         {
846           GNUNET_break (0);
847           goto cleanup;
848         }
849       if ( (GNUNET_OK !=
850             GNUNET_BIO_read_int64 (rh, &ret->data.file.file_size)) ||
851            (GNUNET_OK !=
852             GNUNET_BIO_read (rh, "fileid", &ret->data.file.file_id, sizeof (GNUNET_HashCode))) )
853         {
854           GNUNET_break (0);
855           goto cleanup;
856         }
857       ret->is_directory = GNUNET_NO;
858       ret->data.file.do_index = GNUNET_YES;
859       ret->data.file.have_hash = GNUNET_YES;
860       ret->data.file.index_start_confirmed = GNUNET_NO;
861       ret->data.file.reader = &GNUNET_FS_data_reader_file_;
862       ret->data.file.reader_cls = GNUNET_FS_make_file_reader_context_ (ret->filename);
863       break;
864     case 3: /* file-index-with-hash-confirmed */
865       if (NULL == ret->filename)
866         {
867           GNUNET_break (0);
868           goto cleanup;
869         }
870       if ( (GNUNET_OK !=
871             GNUNET_BIO_read_int64 (rh, &ret->data.file.file_size)) ||
872            (GNUNET_OK !=
873             GNUNET_BIO_read (rh, "fileid", &ret->data.file.file_id, sizeof (GNUNET_HashCode))) )
874         {
875           GNUNET_break (0);
876           goto cleanup;
877         }
878       ret->is_directory = GNUNET_NO;
879       ret->data.file.do_index = GNUNET_YES;
880       ret->data.file.have_hash = GNUNET_YES;
881       ret->data.file.index_start_confirmed = GNUNET_YES;
882       ret->data.file.reader = &GNUNET_FS_data_reader_file_;
883       ret->data.file.reader_cls = GNUNET_FS_make_file_reader_context_ (ret->filename);
884       break;
885     case 4: /* directory */
886       ret->is_directory = GNUNET_YES;
887       if ( (GNUNET_OK !=
888             GNUNET_BIO_read_int32 (rh, &dsize)) ||
889            (NULL == (ret->data.dir.dir_data = GNUNET_malloc_large (dsize))) ||
890            (GNUNET_OK !=
891             GNUNET_BIO_read (rh, "dir-data", ret->data.dir.dir_data, dsize)) ||
892            (GNUNET_OK !=
893             GNUNET_BIO_read_string (rh, "ent-filename", &filename, 16*1024)) )
894         {
895           GNUNET_break (0);
896           goto cleanup;
897         }
898       ret->data.dir.dir_size = (uint32_t) dsize;
899       if (filename != NULL)
900         {
901           ret->data.dir.entries = deserialize_file_information (h, filename);
902           GNUNET_free (filename);
903           filename = NULL;
904           nxt = ret->data.dir.entries;
905           while (nxt != NULL)
906             {
907               nxt->dir = ret;
908               nxt = nxt->next;
909             }  
910         }
911       break;
912     default:
913       GNUNET_break (0);
914       goto cleanup;
915     }
916   ret->serialization = GNUNET_strdup (fn);
917   if (GNUNET_OK !=
918       GNUNET_BIO_read_string (rh, "nxt-filename", &filename, 16*1024))
919     {
920       GNUNET_break (0);
921       goto cleanup;  
922     }
923   if (filename != NULL)
924     {
925       ret->next = deserialize_file_information (h, filename);
926       GNUNET_free (filename);
927       filename = NULL;
928     }
929   GNUNET_free_non_null (ksks);
930   GNUNET_free_non_null (chks);
931   return ret;
932  cleanup:
933   GNUNET_free_non_null (ksks);
934   GNUNET_free_non_null (chks);
935   GNUNET_free_non_null (filename);
936   GNUNET_FS_file_information_destroy (ret, NULL, NULL);
937   return NULL;
938 }
939
940
941 /**
942  * Using the given serialization filename, try to deserialize
943  * the file-information tree associated with it.
944  *
945  * @param h master context
946  * @param filename name of the file (without directory) with
947  *        the infromation
948  * @return NULL on error
949  */
950 static struct GNUNET_FS_FileInformation *
951 deserialize_file_information (struct GNUNET_FS_Handle *h,
952                               const char *filename)
953 {
954   struct GNUNET_FS_FileInformation *ret;
955   struct GNUNET_BIO_ReadHandle *rh;
956   char *emsg;
957
958   rh = get_read_handle (h, GNUNET_FS_SYNC_PATH_FILE_INFO, filename);
959   if (rh == NULL)
960     return NULL;
961   ret = deserialize_fi_node (h, filename, rh);
962   if (GNUNET_OK !=
963       GNUNET_BIO_read_close (rh, &emsg))
964     {
965       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
966                   _("Failed to resume publishing information `%s': %s\n"),
967                   filename,
968                   emsg);
969       GNUNET_free (emsg);
970     }
971   if (ret == NULL)
972     {
973       if (0 != UNLINK (filename))
974         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
975                                   "unlink",
976                                   filename);
977     }
978   return ret;
979 }
980
981
982 /**
983  * Given a serialization name (full absolute path), return the
984  * basename of the file (without the path), which must only
985  * consist of the 6 random characters.
986  * 
987  * @param fullname name to extract the basename from
988  * @return copy of the basename, NULL on error
989  */
990 static char *
991 get_serialization_short_name (const char *fullname)
992 {
993   const char *end;
994   const char *nxt;
995
996   end = NULL;
997   nxt = fullname;
998   /* FIXME: we could do this faster since we know
999      the length of 'end'... */
1000   while ('\0' != *nxt)
1001     {
1002       if (DIR_SEPARATOR == *nxt)
1003         end = nxt + 1;
1004       nxt++;
1005     }
1006   if ( (end == NULL) ||
1007        (strlen (end) == 0) )
1008     {
1009       GNUNET_break (0);
1010       return NULL;
1011     }
1012   GNUNET_break (6 == strlen (end));
1013   return GNUNET_strdup (end);  
1014 }
1015
1016
1017 /**
1018  * Create a new random name for serialization.  Also checks if persistence
1019  * is enabled and returns NULL if not.
1020  *
1021  * @param h master context
1022  * @param ext component of the path 
1023  * @return NULL on errror
1024  */
1025 static char *
1026 make_serialization_file_name (struct GNUNET_FS_Handle *h,
1027                               const char *ext)
1028 {
1029   char *fn;
1030   char *dn;
1031   char *ret;
1032
1033   if (0 == (h->flags & GNUNET_FS_FLAGS_PERSISTENCE))
1034     return NULL; /* persistence not requested */
1035   dn = get_serialization_file_name (h, ext, "");
1036   if (dn == NULL)
1037     return NULL;
1038   if (GNUNET_OK !=
1039       GNUNET_DISK_directory_create_for_file (dn))
1040     {
1041       GNUNET_free (dn);
1042       return NULL;
1043     }
1044   fn = GNUNET_DISK_mktemp (dn);
1045   GNUNET_free (dn);
1046   if (fn == NULL)
1047     return NULL; /* epic fail */
1048   ret = get_serialization_short_name (fn);
1049   GNUNET_free (fn);
1050   return ret;
1051 }
1052
1053
1054 /**
1055  * Create a new random name for serialization.  Also checks if persistence
1056  * is enabled and returns NULL if not.
1057  *
1058  * @param h master context
1059  * @param ext component of the path 
1060  * @param uni name of parent
1061  * @return NULL on errror
1062  */
1063 static char *
1064 make_serialization_file_name_in_dir (struct GNUNET_FS_Handle *h,
1065                                      const char *ext,
1066                                      const char *uni)
1067 {
1068   char *fn;
1069   char *dn;
1070   char *ret;
1071
1072   if (0 == (h->flags & GNUNET_FS_FLAGS_PERSISTENCE))
1073     return NULL; /* persistence not requested */
1074   dn = get_serialization_file_name_in_dir (h, ext, uni, "");
1075   if (dn == NULL)
1076     return NULL;
1077   if (GNUNET_OK !=
1078       GNUNET_DISK_directory_create_for_file (dn))
1079     {
1080       GNUNET_free (dn);
1081       return NULL;
1082     }
1083   fn = GNUNET_DISK_mktemp (dn);
1084   GNUNET_free (dn);
1085   if (fn == NULL)
1086     return NULL; /* epic fail */
1087   ret = get_serialization_short_name (fn);
1088   GNUNET_free (fn);
1089   return ret;
1090 }
1091
1092
1093 /**
1094  * Copy all of the data from the reader to the write handle.
1095  *
1096  * @param wh write handle
1097  * @param fi file with reader
1098  * @return GNUNET_OK on success
1099  */
1100 static int
1101 copy_from_reader (struct GNUNET_BIO_WriteHandle *wh,
1102                   struct GNUNET_FS_FileInformation * fi)
1103 {
1104   char buf[32 * 1024];
1105   uint64_t off;
1106   size_t ret;
1107   size_t left;
1108   char *emsg;
1109
1110   emsg = NULL;
1111   off = 0;
1112   while (off < fi->data.file.file_size)
1113     {
1114       left = GNUNET_MIN (sizeof(buf), fi->data.file.file_size - off);
1115       ret = fi->data.file.reader (fi->data.file.reader_cls,
1116                                   off, left,
1117                                   buf,
1118                                   &emsg);
1119       if (ret == 0)
1120         {
1121           GNUNET_free (emsg);
1122           return GNUNET_SYSERR;
1123         }
1124       if (GNUNET_OK != 
1125           GNUNET_BIO_write (wh, buf, ret))
1126         return GNUNET_SYSERR;
1127       off += ret;
1128     }
1129   return GNUNET_OK;
1130 }
1131
1132
1133 /**
1134  * Create a temporary file on disk to store the current
1135  * state of "fi" in.
1136  *
1137  * @param fi file information to sync with disk
1138  */
1139 void
1140 GNUNET_FS_file_information_sync_ (struct GNUNET_FS_FileInformation * fi)
1141 {
1142   char *fn;
1143   struct GNUNET_BIO_WriteHandle *wh;
1144   char b;
1145   char *ksks;
1146   char *chks;
1147
1148   if (NULL == fi->serialization)    
1149     fi->serialization = make_serialization_file_name (fi->h, GNUNET_FS_SYNC_PATH_FILE_INFO);
1150   if (NULL == fi->serialization)
1151     return;
1152   wh = get_write_handle (fi->h, GNUNET_FS_SYNC_PATH_FILE_INFO, fi->serialization);
1153   if (wh == NULL)
1154     {
1155       GNUNET_free (fi->serialization);
1156       fi->serialization = NULL;
1157       return;
1158     }
1159   if (GNUNET_YES == fi->is_directory)
1160     b = 4;
1161   else if (GNUNET_YES == fi->data.file.index_start_confirmed)
1162     b = 3;
1163   else if (GNUNET_YES == fi->data.file.have_hash)
1164     b = 2;
1165   else if (GNUNET_YES == fi->data.file.do_index)
1166     b = 1;
1167   else
1168     b = 0;
1169   if (fi->keywords != NULL)
1170     ksks = GNUNET_FS_uri_to_string (fi->keywords);
1171   else
1172     ksks = NULL;
1173   if (fi->chk_uri != NULL)
1174     chks = GNUNET_FS_uri_to_string (fi->chk_uri);
1175   else
1176     chks = NULL;
1177   if ( (GNUNET_OK !=
1178         GNUNET_BIO_write (wh, &b, sizeof (b))) ||
1179        (GNUNET_OK != 
1180         GNUNET_BIO_write_meta_data (wh, fi->meta)) ||
1181        (GNUNET_OK !=
1182         GNUNET_BIO_write_string (wh, ksks)) ||
1183        (GNUNET_OK !=
1184         GNUNET_BIO_write_string (wh, chks)) ||
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_int64 (wh, fi->bo.expiration_time.abs_value)) ||
1193        (GNUNET_OK != 
1194         GNUNET_BIO_write_int32 (wh, fi->bo.anonymity_level)) ||
1195        (GNUNET_OK != 
1196         GNUNET_BIO_write_int32 (wh, fi->bo.content_priority)) ||
1197        (GNUNET_OK != 
1198         GNUNET_BIO_write_int32 (wh, fi->bo.replication_level)) )
1199     {
1200       GNUNET_break (0);
1201       goto cleanup;
1202     }
1203   GNUNET_free_non_null (chks);
1204   chks = NULL;
1205   GNUNET_free_non_null (ksks);
1206   ksks = NULL;
1207   
1208   switch (b)
1209     {
1210     case 0: /* file-insert */
1211       if (GNUNET_OK !=
1212           GNUNET_BIO_write_int64 (wh, fi->data.file.file_size))
1213         {
1214           GNUNET_break (0);
1215           goto cleanup;
1216         }
1217       if ( (GNUNET_NO == fi->is_published) &&
1218            (NULL == fi->filename) )     
1219         if (GNUNET_OK != 
1220             copy_from_reader (wh, fi))
1221           {
1222             GNUNET_break (0);
1223             goto cleanup;
1224           }
1225       break;
1226     case 1: /* file-index, no hash */
1227       if (NULL == fi->filename)
1228         {
1229           GNUNET_break (0);
1230           goto cleanup;
1231         }
1232       if (GNUNET_OK !=
1233           GNUNET_BIO_write_int64 (wh, fi->data.file.file_size))
1234         {
1235           GNUNET_break (0);
1236           goto cleanup;
1237         }
1238       break;
1239     case 2: /* file-index-with-hash */
1240     case 3: /* file-index-with-hash-confirmed */
1241       if (NULL == fi->filename)
1242         {
1243           GNUNET_break (0);
1244           goto cleanup;
1245         }
1246       if ( (GNUNET_OK !=
1247             GNUNET_BIO_write_int64 (wh, fi->data.file.file_size)) ||
1248            (GNUNET_OK !=
1249             GNUNET_BIO_write (wh, &fi->data.file.file_id, sizeof (GNUNET_HashCode))) )
1250         {
1251           GNUNET_break (0);
1252           goto cleanup;
1253         }
1254       break;
1255     case 4: /* directory */
1256       if ( (GNUNET_OK !=
1257             GNUNET_BIO_write_int32 (wh, fi->data.dir.dir_size)) ||
1258            (GNUNET_OK !=
1259             GNUNET_BIO_write (wh, fi->data.dir.dir_data, (uint32_t) fi->data.dir.dir_size)) ||
1260            (GNUNET_OK !=
1261             GNUNET_BIO_write_string (wh, 
1262                                      (fi->data.dir.entries == NULL) 
1263                                      ?  NULL
1264                                      : fi->data.dir.entries->serialization)) )
1265         {
1266           GNUNET_break (0);
1267           goto cleanup;
1268         }
1269       break;
1270     default:
1271       GNUNET_assert (0);
1272       goto cleanup;
1273     }
1274   if (GNUNET_OK !=
1275       GNUNET_BIO_write_string (wh, (fi->next != NULL) ? fi->next->serialization : NULL))
1276     {
1277       GNUNET_break (0);           
1278       goto cleanup;  
1279     }
1280   if (GNUNET_OK !=
1281       GNUNET_BIO_write_close (wh))
1282     {
1283       wh = NULL;
1284       GNUNET_break (0);
1285       goto cleanup;
1286     }
1287   return; /* done! */
1288  cleanup:
1289   if (wh != NULL)
1290     (void) GNUNET_BIO_write_close (wh);
1291   GNUNET_free_non_null (chks);
1292   GNUNET_free_non_null (ksks);
1293   fn = get_serialization_file_name (fi->h, GNUNET_FS_SYNC_PATH_FILE_INFO, fi->serialization);
1294   if (NULL != fn)
1295     {
1296       if (0 != UNLINK (fn))
1297         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
1298       GNUNET_free (fn);
1299     }
1300   GNUNET_free (fi->serialization);
1301   fi->serialization = NULL;  
1302 }
1303
1304
1305
1306 /**
1307  * Find the entry in the file information struct where the
1308  * serialization filename matches the given name.
1309  *
1310  * @param pos file information to search
1311  * @param srch filename to search for
1312  * @return NULL if srch was not found in this subtree
1313  */
1314 static struct GNUNET_FS_FileInformation *
1315 find_file_position (struct GNUNET_FS_FileInformation *pos,
1316                     const char *srch)
1317 {
1318   struct GNUNET_FS_FileInformation *r;
1319
1320   while (pos != NULL)
1321     {
1322       if (0 == strcmp (srch,
1323                        pos->serialization))
1324         return pos;
1325       if (pos->is_directory)
1326         {
1327           r = find_file_position (pos->data.dir.entries,
1328                                   srch);
1329           if (r != NULL)
1330             return r;
1331         }
1332       pos = pos->next;
1333     }
1334   return NULL;
1335 }
1336
1337
1338 /**
1339  * Signal the FS's progress function that we are resuming
1340  * an upload.
1341  *
1342  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
1343  * @param fi the entry in the publish-structure
1344  * @param length length of the file or directory
1345  * @param meta metadata for the file or directory (can be modified)
1346  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1347  * @param bo block options (can be modified)
1348  * @param do_index should we index?
1349  * @param client_info pointer to client context set upon creation (can be modified)
1350  * @return GNUNET_OK to continue (always)
1351  */
1352 static int
1353 fip_signal_resume(void *cls,
1354                   struct GNUNET_FS_FileInformation *fi,
1355                   uint64_t length,
1356                   struct GNUNET_CONTAINER_MetaData *meta,
1357                   struct GNUNET_FS_Uri **uri,
1358                   struct GNUNET_FS_BlockOptions *bo,
1359                   int *do_index,
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   if (NULL == fi_root)
1428     {
1429       GNUNET_break (0);
1430       goto cleanup;    
1431     }
1432   pc->fi = deserialize_file_information (h, fi_root);
1433   if (pc->fi == NULL)
1434     {
1435       GNUNET_break (0);
1436       goto cleanup;    
1437     }
1438   if (ns != NULL)
1439     {
1440       pc->namespace = GNUNET_FS_namespace_create (h, ns);
1441       if (pc->namespace == NULL)
1442         {
1443           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1444                       _("Failed to recover namespace `%s', cannot resume publishing operation.\n"),
1445                       ns);
1446           goto cleanup;
1447         }
1448     }
1449   if ( (0 == (pc->options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY)) &&
1450        (GNUNET_YES != pc->all_done) )
1451     {
1452       pc->dsh = GNUNET_DATASTORE_connect (h->cfg);
1453       if (NULL == pc->dsh)
1454         goto cleanup;
1455     } 
1456   if (fi_pos != NULL)
1457     {
1458       pc->fi_pos = find_file_position (pc->fi,
1459                                        fi_pos);
1460       GNUNET_free (fi_pos);
1461       fi_pos = NULL;
1462       if (pc->fi_pos == NULL)
1463         {
1464           /* failed to find position for resuming, outch! Will start from root! */
1465           GNUNET_break (0);
1466           if (pc->all_done != GNUNET_YES)
1467             pc->fi_pos = pc->fi;
1468         }
1469     }
1470   GNUNET_free (fi_root);
1471   fi_root = NULL;
1472   /* generate RESUME event(s) */
1473   GNUNET_FS_file_information_inspect (pc->fi,
1474                                       &fip_signal_resume,
1475                                       pc);
1476   
1477   /* re-start publishing (if needed)... */
1478   if (pc->all_done != GNUNET_YES)
1479     pc->upload_task 
1480       = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
1481                                             &GNUNET_FS_publish_main_,
1482                                             pc);       
1483   if (GNUNET_OK !=
1484       GNUNET_BIO_read_close (rh, &emsg))
1485     {
1486       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1487                   _("Failure while resuming publishing operation `%s': %s\n"),
1488                   filename,
1489                   emsg);
1490       GNUNET_free (emsg);
1491     }
1492   GNUNET_free_non_null (ns);
1493   pc->top = GNUNET_FS_make_top (h, &GNUNET_FS_publish_signal_suspend_, pc);
1494   return GNUNET_OK;
1495  cleanup:
1496   GNUNET_free_non_null (pc->nid);
1497   GNUNET_free_non_null (pc->nuid);
1498   GNUNET_free_non_null (fi_root);
1499   GNUNET_free_non_null (fi_pos);
1500   GNUNET_free_non_null (ns);
1501   if ( (rh != NULL) &&
1502        (GNUNET_OK !=
1503         GNUNET_BIO_read_close (rh, &emsg)) )
1504     {
1505       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1506                   _("Failed to resume publishing operation `%s': %s\n"),
1507                   filename,
1508                   emsg);
1509       GNUNET_free (emsg);
1510     }
1511   if (pc->fi != NULL)
1512     GNUNET_FS_file_information_destroy (pc->fi, NULL, NULL);
1513   if (0 != UNLINK (filename))
1514     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", filename);
1515   GNUNET_free (pc->serialization);
1516   GNUNET_free (pc);
1517   return GNUNET_OK;
1518 }
1519
1520
1521 /**
1522  * Synchronize this publishing struct with its mirror
1523  * on disk.  Note that all internal FS-operations that change
1524  * publishing structs should already call "sync" internally,
1525  * so this function is likely not useful for clients.
1526  * 
1527  * @param pc the struct to sync
1528  */
1529 void
1530 GNUNET_FS_publish_sync_ (struct GNUNET_FS_PublishContext *pc)
1531 {  
1532   struct GNUNET_BIO_WriteHandle *wh;
1533
1534   if (NULL == pc->serialization)
1535     pc->serialization = make_serialization_file_name (pc->h,
1536                                                       GNUNET_FS_SYNC_PATH_MASTER_PUBLISH);
1537   if (NULL == pc->serialization)
1538     return;
1539   if (NULL == pc->fi)
1540     return;
1541   if (NULL == pc->fi->serialization)
1542     {
1543       GNUNET_break (0);
1544       return;
1545     }
1546   wh = get_write_handle (pc->h, GNUNET_FS_SYNC_PATH_MASTER_PUBLISH, pc->serialization);
1547   if (wh == NULL)
1548     {
1549       GNUNET_break (0);
1550       goto cleanup;
1551     }
1552   if ( (GNUNET_OK !=
1553         GNUNET_BIO_write_string (wh, pc->nid)) ||
1554        (GNUNET_OK !=
1555         GNUNET_BIO_write_string (wh, pc->nuid)) ||
1556        (GNUNET_OK !=
1557         GNUNET_BIO_write_int32 (wh, pc->options)) ||
1558        (GNUNET_OK !=
1559         GNUNET_BIO_write_int32 (wh, pc->all_done)) ||
1560        (GNUNET_OK !=
1561         GNUNET_BIO_write_string (wh, pc->fi->serialization)) ||
1562        (GNUNET_OK !=
1563         GNUNET_BIO_write_string (wh, (pc->fi_pos == NULL) ? NULL : pc->fi_pos->serialization)) ||
1564        (GNUNET_OK !=
1565         GNUNET_BIO_write_string (wh, (pc->namespace == NULL) ? NULL : pc->namespace->name)) )
1566    {
1567      GNUNET_break (0);
1568      goto cleanup;
1569    }
1570  if (GNUNET_OK !=
1571      GNUNET_BIO_write_close (wh))
1572    {
1573      wh = NULL;
1574      GNUNET_break (0);
1575      goto cleanup;
1576    }
1577  return;
1578  cleanup:
1579  if (wh != NULL)
1580      (void) GNUNET_BIO_write_close (wh); 
1581  GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_MASTER_PUBLISH, pc->serialization);
1582  GNUNET_free (pc->serialization);
1583  pc->serialization = NULL;
1584 }
1585
1586
1587 /**
1588  * Synchronize this unindex struct with its mirror
1589  * on disk.  Note that all internal FS-operations that change
1590  * publishing structs should already call "sync" internally,
1591  * so this function is likely not useful for clients.
1592  * 
1593  * @param uc the struct to sync
1594  */
1595 void
1596 GNUNET_FS_unindex_sync_ (struct GNUNET_FS_UnindexContext *uc)
1597 {
1598   struct GNUNET_BIO_WriteHandle *wh;
1599
1600   if (NULL == uc->serialization)
1601     uc->serialization = make_serialization_file_name (uc->h,
1602                                                       GNUNET_FS_SYNC_PATH_MASTER_UNINDEX);
1603   if (NULL == uc->serialization)
1604     return;
1605   wh = get_write_handle (uc->h, GNUNET_FS_SYNC_PATH_MASTER_UNINDEX, uc->serialization);
1606   if (wh == NULL)
1607     {
1608       GNUNET_break (0);
1609       goto cleanup;
1610     }
1611   if ( (GNUNET_OK !=
1612         GNUNET_BIO_write_string (wh, uc->filename)) ||
1613        (GNUNET_OK !=
1614         GNUNET_BIO_write_int64 (wh, uc->file_size)) ||
1615        (GNUNET_OK !=
1616         write_start_time (wh, uc->start_time)) ||
1617        (GNUNET_OK !=
1618         GNUNET_BIO_write_int32 (wh, (uint32_t) uc->state)) ||
1619        ( (uc->state == UNINDEX_STATE_FS_NOTIFY) &&
1620          (GNUNET_OK !=
1621           GNUNET_BIO_write (wh, &uc->file_id, sizeof (GNUNET_HashCode))) ) ||
1622        ( (uc->state == UNINDEX_STATE_ERROR) &&
1623          (GNUNET_OK !=
1624           GNUNET_BIO_write_string (wh, uc->emsg)) ) )
1625     {
1626       GNUNET_break (0);
1627       goto cleanup;
1628     }
1629   if (GNUNET_OK !=
1630       GNUNET_BIO_write_close (wh))
1631     {
1632       wh = NULL;
1633       GNUNET_break (0);
1634       goto cleanup;
1635     }  
1636   return;
1637  cleanup:
1638   if (wh != NULL)
1639     (void) GNUNET_BIO_write_close (wh);
1640   GNUNET_FS_remove_sync_file_ (uc->h, GNUNET_FS_SYNC_PATH_MASTER_UNINDEX, uc->serialization);
1641   GNUNET_free (uc->serialization);
1642   uc->serialization = NULL;
1643 }
1644
1645
1646 /**
1647  * Serialize a download request.
1648  * 
1649  * @param wh the 'struct GNUNET_BIO_WriteHandle*'
1650  * @param dr the 'struct DownloadRequest'
1651  * @return GNUNET_YES on success, GNUNET_NO on error
1652  */
1653 static int
1654 write_download_request (struct GNUNET_BIO_WriteHandle *wh,
1655                         struct DownloadRequest *dr)
1656
1657   unsigned int i;
1658   
1659   if ( (GNUNET_OK !=
1660         GNUNET_BIO_write_int32 (wh, dr->state)) ||
1661        (GNUNET_OK !=
1662         GNUNET_BIO_write_int64 (wh, dr->offset)) ||
1663        (GNUNET_OK !=
1664         GNUNET_BIO_write_int32 (wh, dr->num_children)) ||
1665        (GNUNET_OK !=
1666         GNUNET_BIO_write_int32 (wh, dr->depth)) ) 
1667     return GNUNET_NO;    
1668   if ( (dr->state == BRS_CHK_SET) &&
1669        (GNUNET_OK !=
1670         GNUNET_BIO_write (wh, &dr->chk, sizeof (struct ContentHashKey))) )
1671     return GNUNET_NO;
1672   for (i=0;i<dr->num_children;i++)
1673     if (GNUNET_NO == 
1674         write_download_request (wh, dr->children[i]))
1675       return GNUNET_NO;
1676   return GNUNET_YES;
1677 }
1678
1679
1680 /**
1681  * Read a download request tree.
1682  * 
1683  * @param rh stream to read from
1684  * @return value the 'struct DownloadRequest', NULL on error
1685  */
1686 static struct DownloadRequest *
1687 read_download_request (struct GNUNET_BIO_ReadHandle *rh)
1688
1689   struct DownloadRequest *dr;
1690   unsigned int i;
1691
1692   dr = GNUNET_malloc (sizeof (struct DownloadRequest));
1693
1694   if ( (GNUNET_OK !=
1695         GNUNET_BIO_read_int32 (rh, &dr->state)) ||
1696        (GNUNET_OK !=
1697         GNUNET_BIO_read_int64 (rh, &dr->offset)) ||
1698        (GNUNET_OK !=
1699         GNUNET_BIO_read_int32 (rh, &dr->num_children)) ||
1700        (dr->num_children > CHK_PER_INODE) ||
1701        (GNUNET_OK !=
1702         GNUNET_BIO_read_int32 (rh, &dr->depth)) ||
1703        ( (dr->depth == 0) && (dr->num_children > 0) ) ||
1704        ( (dr->depth > 0) && (dr->num_children == 0) ) )
1705     {
1706       GNUNET_break (0);
1707       dr->num_children = 0;
1708       goto cleanup;        
1709     }
1710   if (dr->num_children > 0)
1711     dr->children = GNUNET_malloc (dr->num_children *
1712                                   sizeof (struct ContentHashKey));
1713   switch (dr->state)
1714     {
1715     case BRS_INIT:
1716     case BRS_RECONSTRUCT_DOWN:
1717     case BRS_RECONSTRUCT_META_UP:
1718     case BRS_RECONSTRUCT_UP:    
1719       break;
1720     case BRS_CHK_SET:
1721       if (GNUNET_OK !=
1722           GNUNET_BIO_read (rh, "chk", &dr->chk, sizeof (struct ContentHashKey))) 
1723         goto cleanup;  
1724       break;
1725     case BRS_DOWNLOAD_DOWN:
1726     case BRS_DOWNLOAD_UP:
1727     case BRS_ERROR:
1728       break;
1729     default:
1730       GNUNET_break (0);
1731       goto cleanup;
1732     }
1733   for (i=0;i<dr->num_children;i++)
1734     {
1735       if (NULL == (dr->children[i] = read_download_request (rh)))
1736         goto cleanup;
1737       dr->children[i]->parent = dr;
1738     }
1739   return dr;
1740  cleanup:
1741   GNUNET_FS_free_download_request_ (dr);
1742   return NULL;
1743 }
1744
1745
1746 /**
1747  * Compute the name of the sync file (or directory) for the given download
1748  * context.
1749  *
1750  * @param dc download context to compute for
1751  * @param uni unique filename to use, use "" for the directory name
1752  * @param ext extension to use, use ".dir" for our own subdirectory
1753  * @return the expanded file name, NULL for none
1754  */
1755 static char *
1756 get_download_sync_filename (struct GNUNET_FS_DownloadContext *dc,
1757                             const char *uni,
1758                             const char *ext)
1759 {
1760   char *par;
1761   char *epar;
1762
1763   if (dc->parent == NULL)
1764     return get_serialization_file_name (dc->h,
1765                                         (dc->search != NULL) ?
1766                                         GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD :
1767                                         GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
1768                                         uni);
1769   if (dc->parent->serialization == NULL)
1770     return NULL;
1771   par = get_download_sync_filename (dc->parent, dc->parent->serialization, "");
1772   if (par == NULL)
1773     return NULL;
1774   GNUNET_asprintf (&epar,
1775                    "%s.dir%s%s%s",
1776                    par,
1777                    DIR_SEPARATOR_STR,
1778                    uni,
1779                    ext);
1780   GNUNET_free (par);
1781   return epar;
1782 }
1783
1784
1785 /**
1786  * Synchronize this download struct with its mirror
1787  * on disk.  Note that all internal FS-operations that change
1788  * publishing structs should already call "sync" internally,
1789  * so this function is likely not useful for clients.
1790  * 
1791  * @param dc the struct to sync
1792  */
1793 void
1794 GNUNET_FS_download_sync_ (struct GNUNET_FS_DownloadContext *dc)
1795 {
1796   struct GNUNET_BIO_WriteHandle *wh;
1797   char *uris;
1798   char *fn;
1799   char *dir;
1800
1801   if (NULL == dc->serialization)    
1802     {
1803       dir = get_download_sync_filename (dc, "", "");
1804       if (dir == NULL)
1805         return;
1806       if (GNUNET_OK !=
1807           GNUNET_DISK_directory_create_for_file (dir))
1808         {
1809           GNUNET_free (dir);
1810           return;
1811         }
1812       fn = GNUNET_DISK_mktemp (dir);
1813       GNUNET_free (dir);
1814       if (fn == NULL)
1815         return;
1816       dc->serialization = get_serialization_short_name (fn);
1817     }
1818   else
1819     {
1820       fn = get_download_sync_filename (dc, dc->serialization, "");
1821       if (fn == NULL)
1822         {
1823           GNUNET_free (dc->serialization);
1824           dc->serialization = NULL;
1825           GNUNET_free (fn);
1826           return;
1827         }
1828     }
1829   wh = GNUNET_BIO_write_open (fn);
1830   if (wh == NULL)
1831     {
1832       GNUNET_free (dc->serialization);
1833       dc->serialization = NULL;
1834       GNUNET_free (fn);
1835       return;
1836     }
1837   GNUNET_assert ( (GNUNET_YES == GNUNET_FS_uri_test_chk (dc->uri)) ||
1838                   (GNUNET_YES == GNUNET_FS_uri_test_loc (dc->uri)) );
1839   uris = GNUNET_FS_uri_to_string (dc->uri);
1840   if ( (GNUNET_OK !=
1841         GNUNET_BIO_write_string (wh, uris)) ||
1842        (GNUNET_OK !=
1843         GNUNET_BIO_write_meta_data (wh, dc->meta)) ||
1844        (GNUNET_OK !=
1845         GNUNET_BIO_write_string (wh, dc->emsg)) ||
1846        (GNUNET_OK !=
1847         GNUNET_BIO_write_string (wh, dc->filename)) ||
1848        (GNUNET_OK !=
1849         GNUNET_BIO_write_string (wh, dc->temp_filename)) ||
1850        (GNUNET_OK !=
1851         GNUNET_BIO_write_int64 (wh, dc->old_file_size)) ||
1852        (GNUNET_OK !=
1853         GNUNET_BIO_write_int64 (wh, dc->offset)) ||
1854        (GNUNET_OK !=
1855         GNUNET_BIO_write_int64 (wh, dc->length)) ||
1856        (GNUNET_OK !=
1857         GNUNET_BIO_write_int64 (wh, dc->completed)) ||
1858        (GNUNET_OK !=
1859         write_start_time (wh, dc->start_time)) ||
1860        (GNUNET_OK !=
1861         GNUNET_BIO_write_int32 (wh, dc->anonymity)) ||
1862        (GNUNET_OK !=
1863         GNUNET_BIO_write_int32 (wh, (uint32_t) dc->options)) ||
1864        (GNUNET_OK !=
1865         GNUNET_BIO_write_int32 (wh, (uint32_t) dc->has_finished)) )
1866     {
1867       GNUNET_break (0);           
1868       goto cleanup; 
1869     }
1870   if (NULL == dc->emsg)
1871     {
1872       GNUNET_assert (dc->top_request != NULL);
1873       if (GNUNET_YES !=
1874           write_download_request (wh, dc->top_request))
1875         {
1876           GNUNET_break (0);
1877           goto cleanup;
1878         }
1879     }
1880   GNUNET_free_non_null (uris);
1881   uris = NULL;
1882   if (GNUNET_OK !=
1883       GNUNET_BIO_write_close (wh))
1884     {
1885       wh = NULL;
1886       GNUNET_break (0);
1887       goto cleanup;
1888     }
1889   GNUNET_free (fn);
1890   return;
1891  cleanup:
1892   if (NULL != wh)
1893     (void) GNUNET_BIO_write_close (wh);
1894   GNUNET_free_non_null (uris);
1895   if (0 != UNLINK (fn))
1896     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
1897   GNUNET_free (fn);
1898   GNUNET_free (dc->serialization);
1899   dc->serialization = NULL;
1900 }
1901
1902
1903 /**
1904  * Synchronize this search result with its mirror
1905  * on disk.  Note that all internal FS-operations that change
1906  * publishing structs should already call "sync" internally,
1907  * so this function is likely not useful for clients.
1908  * 
1909  * @param sr the struct to sync
1910  */
1911 void
1912 GNUNET_FS_search_result_sync_ (struct GNUNET_FS_SearchResult *sr)
1913 {
1914   struct GNUNET_BIO_WriteHandle *wh;
1915   char *uris;
1916
1917   uris = NULL;
1918   if (NULL == sr->serialization)
1919     sr->serialization = make_serialization_file_name_in_dir (sr->sc->h,
1920                                                              (sr->sc->psearch_result == NULL) 
1921                                                              ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
1922                                                              : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
1923                                                              sr->sc->serialization);
1924   if (NULL == sr->serialization)
1925     return;
1926   wh = get_write_handle_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   if (wh == NULL)
1933     {
1934       GNUNET_break (0);
1935       goto cleanup;
1936     }
1937   uris = GNUNET_FS_uri_to_string (sr->uri);
1938   if ( (GNUNET_OK !=
1939         GNUNET_BIO_write_string (wh, uris)) ||
1940        (GNUNET_OK !=
1941         GNUNET_BIO_write_string (wh, sr->download != NULL ? sr->download->serialization : NULL)) ||
1942        (GNUNET_OK !=
1943         GNUNET_BIO_write_string (wh, sr->update_search != NULL ? sr->update_search->serialization : NULL)) ||
1944        (GNUNET_OK !=
1945         GNUNET_BIO_write_meta_data (wh, sr->meta)) ||
1946        (GNUNET_OK !=
1947         GNUNET_BIO_write (wh, &sr->key, sizeof (GNUNET_HashCode))) ||
1948        (GNUNET_OK !=
1949         GNUNET_BIO_write_int32 (wh, sr->mandatory_missing)) ||
1950        (GNUNET_OK !=
1951         GNUNET_BIO_write_int32 (wh, sr->optional_support)) ||
1952        (GNUNET_OK !=
1953         GNUNET_BIO_write_int32 (wh, sr->availability_success)) ||
1954        (GNUNET_OK !=
1955         GNUNET_BIO_write_int32 (wh, sr->availability_trials)) )
1956     {
1957       GNUNET_break (0);
1958       goto cleanup;   
1959     }
1960   if (GNUNET_OK !=
1961       GNUNET_BIO_write_close (wh))
1962     {
1963       wh = NULL;
1964       GNUNET_break (0);
1965       goto cleanup;
1966     }
1967   GNUNET_free_non_null (uris);
1968   return;
1969  cleanup:
1970   GNUNET_free_non_null (uris);
1971   if (wh != NULL)
1972     (void)  GNUNET_BIO_write_close (wh);
1973   remove_sync_file_in_dir (sr->sc->h,
1974                            (sr->sc->psearch_result == NULL) 
1975                            ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
1976                            : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
1977                            sr->sc->serialization,
1978                            sr->serialization);
1979   GNUNET_free (sr->serialization);
1980   sr->serialization = NULL;
1981 }
1982
1983
1984 /**
1985  * Synchronize this search struct with its mirror
1986  * on disk.  Note that all internal FS-operations that change
1987  * publishing structs should already call "sync" internally,
1988  * so this function is likely not useful for clients.
1989  * 
1990  * @param sc the struct to sync
1991  */
1992 void
1993 GNUNET_FS_search_sync_ (struct GNUNET_FS_SearchContext *sc)
1994 {  
1995   struct GNUNET_BIO_WriteHandle *wh;
1996   char *uris;
1997   char in_pause;
1998   const char *category;
1999   
2000   category = (sc->psearch_result == NULL) 
2001     ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH 
2002     : GNUNET_FS_SYNC_PATH_CHILD_SEARCH;      
2003   if (NULL == sc->serialization)
2004     sc->serialization = make_serialization_file_name (sc->h,
2005                                                       category);
2006   if (NULL == sc->serialization)
2007     return;
2008   uris = NULL;
2009   wh = get_write_handle (sc->h, category, sc->serialization);
2010   if (wh == NULL)
2011     {
2012       GNUNET_break (0);           
2013       goto cleanup;
2014     }
2015   GNUNET_assert ( (GNUNET_YES == GNUNET_FS_uri_test_ksk (sc->uri)) ||
2016                   (GNUNET_YES == GNUNET_FS_uri_test_sks (sc->uri)) );
2017   uris = GNUNET_FS_uri_to_string (sc->uri);
2018   in_pause = (sc->task != GNUNET_SCHEDULER_NO_TASK) ? 'r' : '\0';
2019   if ( (GNUNET_OK !=
2020         GNUNET_BIO_write_string (wh, uris)) ||
2021        (GNUNET_OK !=
2022         write_start_time (wh, sc->start_time)) ||
2023        (GNUNET_OK !=
2024         GNUNET_BIO_write_string (wh, sc->emsg)) ||
2025        (GNUNET_OK !=
2026         GNUNET_BIO_write_int32 (wh, (uint32_t) sc->options)) ||
2027        (GNUNET_OK !=
2028         GNUNET_BIO_write (wh, &in_pause, sizeof (in_pause))) ||
2029        (GNUNET_OK !=
2030         GNUNET_BIO_write_int32 (wh, sc->anonymity)) )
2031     {
2032       GNUNET_break (0);
2033       goto cleanup;          
2034     }
2035   GNUNET_free (uris);
2036   uris = NULL;
2037   if (GNUNET_OK !=
2038       GNUNET_BIO_write_close (wh))
2039     {
2040       wh = NULL;
2041       GNUNET_break (0);           
2042       goto cleanup;
2043     }
2044   return;
2045  cleanup:
2046   if (wh != NULL)
2047     (void) GNUNET_BIO_write_close (wh);
2048   GNUNET_free_non_null (uris);
2049   GNUNET_FS_remove_sync_file_ (sc->h, category, sc->serialization);
2050   GNUNET_free (sc->serialization);
2051   sc->serialization = NULL;
2052 }
2053
2054
2055 /**
2056  * Function called with a filename of serialized unindexing operation
2057  * to deserialize.
2058  *
2059  * @param cls the 'struct GNUNET_FS_Handle*'
2060  * @param filename complete filename (absolute path)
2061  * @return GNUNET_OK (continue to iterate)
2062  */
2063 static int
2064 deserialize_unindex_file (void *cls,
2065                           const char *filename)
2066 {
2067   struct GNUNET_FS_Handle *h = cls;
2068   struct GNUNET_BIO_ReadHandle *rh;
2069   struct GNUNET_FS_UnindexContext *uc;
2070   struct GNUNET_FS_ProgressInfo pi;
2071   char *emsg;
2072   uint32_t state;
2073
2074   uc = GNUNET_malloc (sizeof (struct GNUNET_FS_UnindexContext));
2075   uc->h = h;
2076   uc->serialization = get_serialization_short_name (filename);
2077   rh = GNUNET_BIO_read_open (filename);
2078   if (rh == NULL)
2079     {
2080       GNUNET_break (0);     
2081       goto cleanup;
2082     }
2083   if ( (GNUNET_OK !=
2084         GNUNET_BIO_read_string (rh, "unindex-fn", &uc->filename, 10*1024)) ||
2085        (GNUNET_OK !=
2086         GNUNET_BIO_read_int64 (rh, &uc->file_size)) ||
2087        (GNUNET_OK !=
2088         read_start_time (rh, &uc->start_time)) ||
2089        (GNUNET_OK !=
2090         GNUNET_BIO_read_int32 (rh, &state)) )
2091     {
2092       GNUNET_break (0);     
2093       goto cleanup;          
2094     }
2095   uc->state = (enum UnindexState) state;
2096   switch (state)
2097     {
2098     case UNINDEX_STATE_HASHING:
2099       break;
2100     case UNINDEX_STATE_FS_NOTIFY:
2101       if (GNUNET_OK !=
2102           GNUNET_BIO_read (rh, "unindex-hash", &uc->file_id, sizeof (GNUNET_HashCode)))
2103         {
2104           GNUNET_break (0);
2105           goto cleanup;
2106         }
2107       break;
2108     case UNINDEX_STATE_DS_REMOVE:
2109       break;
2110     case UNINDEX_STATE_COMPLETE:
2111       break;
2112     case UNINDEX_STATE_ERROR:
2113       if (GNUNET_OK !=
2114           GNUNET_BIO_read_string (rh, "unindex-emsg", &uc->emsg, 10*1024))
2115         {
2116           GNUNET_break (0);
2117           goto cleanup;
2118         }
2119       break;
2120     default:
2121       GNUNET_break (0);
2122       goto cleanup;
2123     }
2124   uc->top = GNUNET_FS_make_top (h,
2125                                 &GNUNET_FS_unindex_signal_suspend_,
2126                                 uc);
2127   pi.status = GNUNET_FS_STATUS_UNINDEX_RESUME;
2128   pi.value.unindex.specifics.resume.message = uc->emsg;
2129   GNUNET_FS_unindex_make_status_ (&pi,
2130                                   uc,
2131                                   (uc->state == UNINDEX_STATE_COMPLETE) 
2132                                   ? uc->file_size
2133                                   : 0);
2134   switch (uc->state)
2135     {
2136     case UNINDEX_STATE_HASHING:
2137       uc->fhc = GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE,
2138                                          uc->filename,
2139                                          HASHING_BLOCKSIZE,
2140                                          &GNUNET_FS_unindex_process_hash_,
2141                                          uc);
2142       break;
2143     case UNINDEX_STATE_FS_NOTIFY:
2144       uc->state = UNINDEX_STATE_HASHING;
2145       GNUNET_FS_unindex_process_hash_ (uc,
2146                                        &uc->file_id);
2147       break;
2148     case UNINDEX_STATE_DS_REMOVE:
2149       GNUNET_FS_unindex_do_remove_ (uc);
2150       break;
2151     case UNINDEX_STATE_COMPLETE:
2152     case UNINDEX_STATE_ERROR:
2153       /* no need to resume any operation, we were done */
2154       break;
2155     default:
2156       break;
2157     }
2158   if (GNUNET_OK !=
2159       GNUNET_BIO_read_close (rh, &emsg))
2160     {
2161       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2162                   _("Failure while resuming unindexing operation `%s': %s\n"),
2163                   filename,
2164                   emsg);
2165       GNUNET_free (emsg);
2166     }
2167   return GNUNET_OK;
2168  cleanup:
2169   GNUNET_free_non_null (uc->filename);
2170   if ( (rh != NULL) &&
2171        (GNUNET_OK !=
2172         GNUNET_BIO_read_close (rh, &emsg)) )
2173     {
2174       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2175                   _("Failed to resume unindexing operation `%s': %s\n"),
2176                   filename,
2177                   emsg);
2178       GNUNET_free (emsg);
2179     }
2180   if (uc->serialization != NULL)
2181     GNUNET_FS_remove_sync_file_ (h, GNUNET_FS_SYNC_PATH_MASTER_UNINDEX, uc->serialization);
2182   GNUNET_free_non_null (uc->serialization);
2183   GNUNET_free (uc);
2184   return GNUNET_OK;
2185 }
2186
2187
2188 /**
2189  * Deserialize a download.
2190  *
2191  * @param h overall context
2192  * @param rh file to deserialize from
2193  * @param parent parent download
2194  * @param search associated search
2195  * @param serialization name under which the search was serialized
2196  */
2197 static void
2198 deserialize_download (struct GNUNET_FS_Handle *h,
2199                       struct GNUNET_BIO_ReadHandle *rh,
2200                       struct GNUNET_FS_DownloadContext *parent,
2201                       struct GNUNET_FS_SearchResult *search,
2202                       const char *serialization);
2203
2204
2205 /**
2206  * Deserialize a search. 
2207  *
2208  * @param h overall context
2209  * @param rh file to deserialize from
2210  * @param psearch_result parent search result
2211  * @param serialization name under which the search was serialized
2212  */
2213 static struct GNUNET_FS_SearchContext *
2214 deserialize_search (struct GNUNET_FS_Handle *h,
2215                     struct GNUNET_BIO_ReadHandle *rh,
2216                     struct GNUNET_FS_SearchResult *psearch_result,
2217                     const char *serialization);
2218
2219
2220 /**
2221  * Function called with a filename of serialized search result
2222  * to deserialize.
2223  *
2224  * @param cls the 'struct GNUNET_FS_SearchContext*'
2225  * @param filename complete filename (absolute path)
2226  * @return GNUNET_OK (continue to iterate)
2227  */
2228 static int
2229 deserialize_search_result (void *cls,
2230                            const char *filename)
2231 {
2232   struct GNUNET_FS_SearchContext *sc = cls;
2233   char *ser;
2234   char *uris;
2235   char *emsg;
2236   char *download;
2237   char *update_srch;
2238   struct GNUNET_BIO_ReadHandle *rh;
2239   struct GNUNET_BIO_ReadHandle *drh;
2240   struct GNUNET_FS_SearchResult *sr;
2241
2242   ser = get_serialization_short_name (filename);
2243   rh = GNUNET_BIO_read_open (filename);
2244   if (rh == NULL)
2245     {
2246       if (ser != NULL)
2247         {
2248           remove_sync_file_in_dir (sc->h, 
2249                                    (sc->psearch_result == NULL) 
2250                                    ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2251                                    : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2252                                    sc->serialization,
2253                                    ser);
2254           GNUNET_free (ser);
2255         }
2256       return GNUNET_OK;
2257     }
2258   emsg = NULL;
2259   uris = NULL;
2260   download = NULL;
2261   update_srch = NULL;
2262   sr = GNUNET_malloc (sizeof (struct GNUNET_FS_SearchResult));
2263   sr->serialization = ser;  
2264   if ( (GNUNET_OK !=
2265         GNUNET_BIO_read_string (rh, "result-uri", &uris, 10*1024)) ||
2266        (NULL == (sr->uri = GNUNET_FS_uri_parse (uris, &emsg))) ||       
2267        (GNUNET_OK !=
2268         GNUNET_BIO_read_string (rh, "download-lnk", &download, 16)) ||
2269        (GNUNET_OK !=
2270         GNUNET_BIO_read_string (rh, "search-lnk", &update_srch, 16)) ||
2271        (GNUNET_OK !=
2272         GNUNET_BIO_read_meta_data (rh, "result-meta", &sr->meta)) ||
2273        (GNUNET_OK !=
2274         GNUNET_BIO_read (rh, "result-key", &sr->key, sizeof (GNUNET_HashCode))) ||
2275        (GNUNET_OK !=
2276         GNUNET_BIO_read_int32 (rh, &sr->mandatory_missing)) ||
2277        (GNUNET_OK !=
2278         GNUNET_BIO_read_int32 (rh, &sr->optional_support)) ||
2279        (GNUNET_OK !=
2280         GNUNET_BIO_read_int32 (rh, &sr->availability_success)) ||
2281        (GNUNET_OK !=
2282         GNUNET_BIO_read_int32 (rh, &sr->availability_trials)) )
2283     {
2284       GNUNET_break (0);
2285       goto cleanup;   
2286     }
2287   GNUNET_free (uris);
2288   if (download != NULL)
2289     {
2290       drh = get_read_handle (sc->h, 
2291                              GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD,
2292                              download);
2293       if (drh != NULL)
2294         {
2295           deserialize_download (sc->h,
2296                                 drh,
2297                                 NULL,
2298                                 sr,
2299                                 download);
2300           if (GNUNET_OK !=
2301               GNUNET_BIO_read_close (drh, &emsg))
2302             {
2303               GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2304                           _("Failed to resume sub-download `%s': %s\n"),
2305                           download,
2306                           emsg);
2307               GNUNET_free (emsg);
2308             }
2309         }
2310       GNUNET_free (download);
2311     }
2312   if (update_srch != NULL)
2313     {
2314       drh = get_read_handle (sc->h, 
2315                              GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2316                              update_srch);
2317       if (drh != NULL)
2318         {
2319           deserialize_search (sc->h,
2320                               drh,
2321                               sr,
2322                               update_srch);
2323           if (GNUNET_OK !=
2324               GNUNET_BIO_read_close (drh, &emsg))
2325             {
2326               GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2327                           _("Failed to resume sub-search `%s': %s\n"),
2328                           update_srch,
2329                           emsg);
2330               GNUNET_free (emsg);
2331             }
2332         }
2333       GNUNET_free (update_srch);     
2334     }
2335   GNUNET_CONTAINER_multihashmap_put (sc->master_result_map,
2336                                      &sr->key,
2337                                      sr,
2338                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
2339   if (GNUNET_OK !=
2340       GNUNET_BIO_read_close (rh, &emsg))
2341     {
2342       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2343                   _("Failure while resuming search operation `%s': %s\n"),
2344                   filename,
2345                   emsg);
2346       GNUNET_free (emsg);
2347     }
2348   return GNUNET_OK;
2349  cleanup:
2350   GNUNET_free_non_null (download);
2351   GNUNET_free_non_null (emsg);
2352   GNUNET_free_non_null (uris);
2353   GNUNET_free_non_null (update_srch);     
2354   if (sr->uri != NULL)
2355     GNUNET_FS_uri_destroy (sr->uri);
2356   if (sr->meta != NULL)
2357     GNUNET_CONTAINER_meta_data_destroy (sr->meta);
2358   GNUNET_free (sr->serialization);
2359   GNUNET_free (sr);  
2360   if (GNUNET_OK !=
2361       GNUNET_BIO_read_close (rh, &emsg))
2362     {
2363       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2364                   _("Failure while resuming search operation `%s': %s\n"),
2365                   filename,
2366                   emsg);
2367       GNUNET_free (emsg);
2368     }
2369   return GNUNET_OK;
2370 }
2371
2372
2373 /**
2374  * Send the 'resume' signal to the callback; also actually
2375  * resume the download (put it in the queue).  Does this
2376  * recursively for the top-level download and all child
2377  * downloads.
2378  * 
2379  * @param dc download to resume
2380  */
2381 static void
2382 signal_download_resume (struct GNUNET_FS_DownloadContext *dc)
2383 {
2384   struct GNUNET_FS_DownloadContext *dcc;
2385   struct GNUNET_FS_ProgressInfo pi;
2386   
2387   pi.status = GNUNET_FS_STATUS_DOWNLOAD_RESUME;
2388   pi.value.download.specifics.resume.meta = dc->meta;
2389   pi.value.download.specifics.resume.message = dc->emsg;
2390   GNUNET_FS_download_make_status_ (&pi,
2391                                    dc);
2392   dcc = dc->child_head;
2393   while (NULL != dcc)
2394     {
2395       signal_download_resume (dcc);
2396       dcc = dcc->next;
2397     }
2398   if (dc->pending_head != NULL)
2399     GNUNET_FS_download_start_downloading_ (dc);
2400 }
2401
2402
2403 /**
2404  * Signal resuming of a search to our clients (for the
2405  * top level search and all sub-searches).
2406  *
2407  * @param sc search being resumed
2408  */
2409 static void
2410 signal_search_resume (struct GNUNET_FS_SearchContext *sc);
2411
2412
2413 /**
2414  * Iterator over search results signaling resume to the client for
2415  * each result.
2416  *
2417  * @param cls closure, the 'struct GNUNET_FS_SearchContext'
2418  * @param key current key code
2419  * @param value value in the hash map, the 'struct GNUNET_FS_SearchResult'
2420  * @return GNUNET_YES (we should continue to iterate)
2421  */
2422 static int
2423 signal_result_resume (void *cls,
2424                       const GNUNET_HashCode * key,
2425                       void *value)
2426 {
2427   struct GNUNET_FS_SearchContext *sc = cls;
2428   struct GNUNET_FS_ProgressInfo pi;
2429   struct GNUNET_FS_SearchResult *sr = value;
2430
2431   if (0 == sr->mandatory_missing)
2432     {
2433       pi.status = GNUNET_FS_STATUS_SEARCH_RESUME_RESULT;
2434       pi.value.search.specifics.resume_result.meta = sr->meta;
2435       pi.value.search.specifics.resume_result.uri = sr->uri;
2436       pi.value.search.specifics.resume_result.result = sr;
2437       pi.value.search.specifics.resume_result.availability_rank = 2*sr->availability_success - sr->availability_trials;
2438       pi.value.search.specifics.resume_result.availability_certainty = sr->availability_trials;
2439       pi.value.search.specifics.resume_result.applicability_rank = sr->optional_support;
2440       sr->client_info = GNUNET_FS_search_make_status_ (&pi,
2441                                                        sc);
2442     }
2443   if (sr->download != NULL)
2444     {
2445       signal_download_resume (sr->download);
2446     }
2447   else
2448     {
2449       GNUNET_FS_search_start_probe_ (sr);
2450     }
2451   if (sr->update_search != NULL)
2452     signal_search_resume (sr->update_search);
2453   return GNUNET_YES;
2454 }
2455
2456
2457 /**
2458  * Free memory allocated by the search context and its children
2459  *
2460  * @param sc search context to free
2461  */
2462 static void
2463 free_search_context (struct GNUNET_FS_SearchContext *sc);
2464
2465
2466 /**
2467  * Iterator over search results freeing each.
2468  *
2469  * @param cls closure, the 'struct GNUNET_FS_SearchContext'
2470  * @param key current key code
2471  * @param value value in the hash map, the 'struct GNUNET_FS_SearchResult'
2472  * @return GNUNET_YES (we should continue to iterate)
2473  */
2474 static int
2475 free_result (void *cls,
2476              const GNUNET_HashCode * key,
2477              void *value)
2478 {
2479   struct GNUNET_FS_SearchResult *sr = value;
2480
2481   if (sr->update_search != NULL)
2482     {
2483       free_search_context (sr->update_search);
2484       GNUNET_assert (NULL == sr->update_search);
2485     }
2486   GNUNET_CONTAINER_meta_data_destroy (sr->meta);
2487   GNUNET_FS_uri_destroy (sr->uri);
2488   GNUNET_free (sr);
2489   return GNUNET_YES;
2490 }
2491
2492
2493 /**
2494  * Free memory allocated by the search context and its children
2495  *
2496  * @param sc search context to free
2497  */
2498 static void
2499 free_search_context (struct GNUNET_FS_SearchContext *sc)
2500 {
2501   if (sc->serialization != NULL)
2502     {
2503       GNUNET_FS_remove_sync_file_ (sc->h,
2504                                    (sc->psearch_result == NULL) 
2505                                    ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2506                                    : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2507                                    sc->serialization);
2508       GNUNET_FS_remove_sync_dir_ (sc->h,
2509                                    (sc->psearch_result == NULL) 
2510                                    ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2511                                    : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2512                                   sc->serialization);
2513     }
2514   GNUNET_free_non_null (sc->serialization);
2515   GNUNET_free_non_null (sc->emsg);
2516   if (sc->uri != NULL)
2517     GNUNET_FS_uri_destroy (sc->uri);
2518   if (sc->master_result_map != NULL)
2519     {
2520       GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
2521                                              &free_result,
2522                                              sc);
2523       GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
2524     }
2525   GNUNET_free (sc);
2526 }
2527
2528
2529 /**
2530  * Function called with a filename of serialized sub-download
2531  * to deserialize.
2532  *
2533  * @param cls the 'struct GNUNET_FS_DownloadContext*' (parent)
2534  * @param filename complete filename (absolute path)
2535  * @return GNUNET_OK (continue to iterate)
2536  */
2537 static int
2538 deserialize_subdownload (void *cls,
2539                          const char *filename)
2540 {
2541   struct GNUNET_FS_DownloadContext *parent = cls;
2542   char *ser;
2543   char *emsg;
2544   struct GNUNET_BIO_ReadHandle *rh;
2545
2546   ser = get_serialization_short_name (filename);
2547   rh = GNUNET_BIO_read_open (filename);
2548   if (rh == NULL)
2549     {
2550       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2551                   _("Failed to resume sub-download `%s': could not open file `%s'\n"),
2552                   ser,
2553                   filename);
2554       GNUNET_free (ser);
2555       return GNUNET_OK;
2556     }
2557   deserialize_download (parent->h,
2558                         rh,
2559                         parent,
2560                         NULL,
2561                         ser);
2562   if (GNUNET_OK !=
2563       GNUNET_BIO_read_close (rh, &emsg))
2564     {
2565       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2566                   _("Failed to resume sub-download `%s': %s\n"),
2567                   ser,
2568                   emsg);
2569       GNUNET_free (emsg);
2570     }
2571   GNUNET_free (ser);
2572   return GNUNET_OK;
2573 }
2574
2575
2576 /**
2577  * Free this download context and all of its descendants.
2578  * (only works during deserialization since not all possible
2579  * state it taken care of).
2580  *
2581  * @param dc context to free
2582  */
2583 static void
2584 free_download_context (struct GNUNET_FS_DownloadContext *dc)
2585 {
2586   struct GNUNET_FS_DownloadContext *dcc;
2587
2588   if (dc->meta != NULL)
2589     GNUNET_CONTAINER_meta_data_destroy (dc->meta);
2590   if (dc->uri != NULL)
2591     GNUNET_FS_uri_destroy (dc->uri);
2592   GNUNET_free_non_null (dc->temp_filename);
2593   GNUNET_free_non_null (dc->emsg);
2594   GNUNET_free_non_null (dc->filename);
2595   GNUNET_free_non_null (dc->serialization);
2596   while (NULL != (dcc = dc->child_head))
2597     {
2598       GNUNET_CONTAINER_DLL_remove (dc->child_head,
2599                                    dc->child_tail,
2600                                    dcc);
2601       free_download_context (dcc);
2602     }
2603   GNUNET_FS_free_download_request_ (dc->top_request);
2604   if (NULL != dc->active)    
2605     GNUNET_CONTAINER_multihashmap_destroy (dc->active);    
2606   GNUNET_free (dc);
2607 }
2608
2609
2610 /**
2611  * Deserialize a download.
2612  *
2613  * @param h overall context
2614  * @param rh file to deserialize from
2615  * @param parent parent download
2616  * @param search associated search
2617  * @param serialization name under which the search was serialized
2618  */
2619 static void
2620 deserialize_download (struct GNUNET_FS_Handle *h,
2621                       struct GNUNET_BIO_ReadHandle *rh,
2622                       struct GNUNET_FS_DownloadContext *parent,
2623                       struct GNUNET_FS_SearchResult *search,
2624                       const char *serialization)
2625 {
2626   struct GNUNET_FS_DownloadContext *dc;
2627   struct DownloadRequest *dr;
2628   char *emsg;
2629   char *uris;
2630   char *dn;
2631   uint32_t options;
2632   uint32_t status;
2633
2634   uris = NULL;
2635   emsg = NULL;
2636   dr = NULL;
2637   dc = GNUNET_malloc (sizeof (struct GNUNET_FS_DownloadContext));
2638   dc->parent = parent;
2639   dc->h = h;
2640   dc->serialization = GNUNET_strdup (serialization);
2641   if ( (GNUNET_OK !=
2642         GNUNET_BIO_read_string (rh, "download-uri", &uris, 10*1024)) ||
2643        (NULL == (dc->uri = GNUNET_FS_uri_parse (uris, &emsg))) ||       
2644        ( (GNUNET_YES != GNUNET_FS_uri_test_chk (dc->uri)) &&
2645          (GNUNET_YES != GNUNET_FS_uri_test_loc (dc->uri)) ) ||
2646        (GNUNET_OK !=
2647         GNUNET_BIO_read_meta_data (rh, "download-meta", &dc->meta)) ||
2648        (GNUNET_OK !=
2649         GNUNET_BIO_read_string (rh, "download-emsg", &dc->emsg, 10*1024)) ||
2650        (GNUNET_OK !=
2651         GNUNET_BIO_read_string (rh, "download-fn", &dc->filename, 10*1024)) ||
2652        (GNUNET_OK !=
2653         GNUNET_BIO_read_string (rh, "download-tfn", &dc->temp_filename, 10*1024)) ||
2654        (GNUNET_OK !=
2655         GNUNET_BIO_read_int64 (rh, &dc->old_file_size)) ||
2656        (GNUNET_OK !=
2657         GNUNET_BIO_read_int64 (rh, &dc->offset)) ||
2658        (GNUNET_OK !=
2659         GNUNET_BIO_read_int64 (rh, &dc->length)) ||
2660        (GNUNET_OK !=
2661         GNUNET_BIO_read_int64 (rh, &dc->completed)) ||
2662        (GNUNET_OK !=
2663         read_start_time (rh, &dc->start_time)) ||
2664        (GNUNET_OK !=
2665         GNUNET_BIO_read_int32 (rh, &dc->anonymity)) ||
2666        (GNUNET_OK !=
2667         GNUNET_BIO_read_int32 (rh, &options)) ||
2668        (GNUNET_OK !=
2669         GNUNET_BIO_read_int32 (rh, &status)) )
2670     {
2671       GNUNET_break (0);
2672       goto cleanup;          
2673     }
2674   dc->options = (enum GNUNET_FS_DownloadOptions) options;
2675   dc->active = GNUNET_CONTAINER_multihashmap_create (1 + 2 * (dc->length / DBLOCK_SIZE));
2676   dc->has_finished = (int) status;
2677   dc->treedepth = GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size (dc->uri));
2678   if (GNUNET_FS_uri_test_loc (dc->uri))
2679     GNUNET_assert (GNUNET_OK ==
2680                    GNUNET_FS_uri_loc_get_peer_identity (dc->uri,
2681                                                         &dc->target));
2682   if (dc->emsg == NULL)
2683     {
2684       dc->top_request = read_download_request (rh);
2685       if (dc->top_request == NULL)
2686         {
2687           GNUNET_break (0);
2688           goto cleanup;
2689         }
2690     }
2691   dn = get_download_sync_filename (dc, dc->serialization, ".dir");
2692   if (dn != NULL)
2693     {
2694       if (GNUNET_YES ==
2695           GNUNET_DISK_directory_test (dn))
2696         GNUNET_DISK_directory_scan (dn, &deserialize_subdownload, dc);
2697       GNUNET_free (dn);
2698     }
2699   if (parent != NULL)
2700     {
2701       abort (); // for debugging for now
2702       GNUNET_CONTAINER_DLL_insert (parent->child_head,
2703                                    parent->child_tail,
2704                                    dc);
2705     }
2706   if (search != NULL)
2707     {
2708       dc->search = search;
2709       search->download = dc;
2710     }
2711   if ( (parent == NULL) &&
2712        (search == NULL) )
2713     {
2714       dc->top = GNUNET_FS_make_top (dc->h,
2715                                     &GNUNET_FS_download_signal_suspend_,
2716                                     dc);      
2717       signal_download_resume (dc);  
2718     }
2719   GNUNET_free (uris);
2720   dc->task 
2721     = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc);
2722   return;
2723  cleanup:
2724   GNUNET_free_non_null (uris);
2725   GNUNET_free_non_null (emsg);
2726   free_download_context (dc);
2727 }
2728
2729
2730 /**
2731  * Signal resuming of a search to our clients (for the
2732  * top level search and all sub-searches).
2733  *
2734  * @param sc search being resumed
2735  */
2736 static void
2737 signal_search_resume (struct GNUNET_FS_SearchContext *sc)
2738 {
2739   struct GNUNET_FS_ProgressInfo pi;
2740
2741   pi.status = GNUNET_FS_STATUS_SEARCH_RESUME;
2742   pi.value.search.specifics.resume.message = sc->emsg;
2743   pi.value.search.specifics.resume.is_paused = (sc->client == NULL) ? GNUNET_YES : GNUNET_NO;
2744   sc->client_info = GNUNET_FS_search_make_status_ (&pi,
2745                                                    sc);
2746   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
2747                                          &signal_result_resume,
2748                                          sc);
2749
2750 }
2751
2752
2753 /**
2754  * Deserialize a search. 
2755  *
2756  * @param h overall context
2757  * @param rh file to deserialize from
2758  * @param psearch_result parent search result
2759  * @param serialization name under which the search was serialized
2760  */
2761 static struct GNUNET_FS_SearchContext *
2762 deserialize_search (struct GNUNET_FS_Handle *h,
2763                     struct GNUNET_BIO_ReadHandle *rh,
2764                     struct GNUNET_FS_SearchResult *psearch_result,
2765                     const char *serialization)
2766 {
2767   struct GNUNET_FS_SearchContext *sc;
2768   char *emsg;
2769   char *uris;
2770   char *dn;
2771   uint32_t options;
2772   char in_pause;
2773
2774   if ( (psearch_result != NULL) &&
2775        (psearch_result->update_search != NULL) )
2776     {
2777       GNUNET_break (0);
2778       return NULL;
2779     }
2780   uris = NULL;
2781   emsg = NULL;
2782   sc = GNUNET_malloc (sizeof (struct GNUNET_FS_SearchContext));
2783   if (psearch_result != NULL)
2784     {
2785       sc->psearch_result = psearch_result;
2786       psearch_result->update_search = sc;
2787     }
2788   sc->h = h;
2789   sc->serialization = GNUNET_strdup (serialization);
2790   if ( (GNUNET_OK !=
2791         GNUNET_BIO_read_string (rh, "search-uri", &uris, 10*1024)) ||
2792        (NULL == (sc->uri = GNUNET_FS_uri_parse (uris, &emsg))) ||       
2793        ( (GNUNET_YES != GNUNET_FS_uri_test_ksk (sc->uri)) &&
2794          (GNUNET_YES != GNUNET_FS_uri_test_sks (sc->uri)) ) ||
2795        (GNUNET_OK !=
2796         read_start_time (rh, &sc->start_time)) ||
2797        (GNUNET_OK !=
2798         GNUNET_BIO_read_string (rh, "search-emsg", &sc->emsg, 10*1024)) ||
2799        (GNUNET_OK !=
2800         GNUNET_BIO_read_int32 (rh, &options)) ||
2801        (GNUNET_OK !=
2802         GNUNET_BIO_read (rh, "search-pause", &in_pause, sizeof (in_pause))) ||
2803        (GNUNET_OK !=
2804         GNUNET_BIO_read_int32 (rh, &sc->anonymity)) )
2805     {
2806       GNUNET_break (0);           
2807       goto cleanup;          
2808     }
2809   sc->options = (enum GNUNET_FS_SearchOptions) options;
2810   sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16);
2811   dn = get_serialization_file_name_in_dir (h,
2812                                            (sc->psearch_result == NULL) 
2813                                            ? GNUNET_FS_SYNC_PATH_MASTER_SEARCH
2814                                            : GNUNET_FS_SYNC_PATH_CHILD_SEARCH,
2815                                            sc->serialization,
2816                                            "");
2817   if (dn != NULL)
2818     {
2819       if (GNUNET_YES ==
2820           GNUNET_DISK_directory_test (dn))
2821         GNUNET_DISK_directory_scan (dn, &deserialize_search_result, sc);
2822       GNUNET_free (dn);
2823     }
2824   if ( ('\0' == in_pause) &&
2825        (GNUNET_OK !=
2826         GNUNET_FS_search_start_searching_ (sc)) )
2827     {
2828       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2829                   _("Could not resume running search, will resume as paused search\n"));    
2830     }
2831   signal_search_resume (sc);
2832   GNUNET_free (uris);
2833   return sc;
2834  cleanup:
2835   GNUNET_free_non_null (emsg);
2836   free_search_context (sc);
2837   GNUNET_free_non_null (uris);
2838   return NULL;
2839 }
2840
2841
2842 /**
2843  * Function called with a filename of serialized search operation
2844  * to deserialize.
2845  *
2846  * @param cls the 'struct GNUNET_FS_Handle*'
2847  * @param filename complete filename (absolute path)
2848  * @return GNUNET_OK (continue to iterate)
2849  */
2850 static int
2851 deserialize_search_file (void *cls,
2852                           const char *filename)
2853 {
2854   struct GNUNET_FS_Handle *h = cls;
2855   char *ser;
2856   char *emsg;
2857   struct GNUNET_BIO_ReadHandle *rh;
2858   struct GNUNET_FS_SearchContext *sc;
2859
2860   ser = get_serialization_short_name (filename);
2861   rh = GNUNET_BIO_read_open (filename);
2862   if (rh == NULL)
2863     {
2864       if (ser != NULL)
2865         {
2866           GNUNET_FS_remove_sync_file_ (h, GNUNET_FS_SYNC_PATH_MASTER_SEARCH, ser);
2867           GNUNET_free (ser);
2868         }
2869       return GNUNET_OK;
2870     }
2871   sc = deserialize_search (h, rh, NULL, ser);
2872   if (sc != NULL)
2873     sc->top = GNUNET_FS_make_top (h, &GNUNET_FS_search_signal_suspend_, sc);
2874   GNUNET_free (ser);
2875   if (GNUNET_OK !=
2876       GNUNET_BIO_read_close (rh, &emsg))
2877     {
2878       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2879                   _("Failure while resuming search operation `%s': %s\n"),
2880                   filename,
2881                   emsg);
2882       GNUNET_free (emsg);
2883     }
2884   return GNUNET_OK;
2885 }
2886
2887
2888 /**
2889  * Function called with a filename of serialized download operation
2890  * to deserialize.
2891  *
2892  * @param cls the 'struct GNUNET_FS_Handle*'
2893  * @param filename complete filename (absolute path)
2894  * @return GNUNET_OK (continue to iterate)
2895  */
2896 static int
2897 deserialize_download_file (void *cls,
2898                            const char *filename)
2899 {
2900   struct GNUNET_FS_Handle *h = cls;
2901   char *ser;
2902   char *emsg;
2903   struct GNUNET_BIO_ReadHandle *rh;
2904
2905   ser = get_serialization_short_name (filename);
2906   rh = GNUNET_BIO_read_open (filename);
2907   if (rh == NULL)
2908     {
2909        if (0 != UNLINK (filename))
2910          GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2911                                    "unlink", 
2912                                    filename);
2913       GNUNET_free (ser);
2914       return GNUNET_OK;
2915     }
2916   deserialize_download (h, rh, NULL, NULL, ser);
2917   GNUNET_free (ser);
2918   if (GNUNET_OK !=
2919       GNUNET_BIO_read_close (rh, &emsg))
2920     {
2921       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2922                   _("Failure while resuming download operation `%s': %s\n"),
2923                   filename,
2924                   emsg);
2925       GNUNET_free (emsg);
2926     }
2927   return GNUNET_OK;
2928 }
2929
2930
2931 /**
2932  * Deserialize informatin about pending operations.
2933  *
2934  * @param master_path which master directory should be scanned
2935  * @param proc function to call for each entry (will get 'h' for 'cls')
2936  * @param h the 'struct GNUNET_FS_Handle*'
2937  */
2938 static void
2939 deserialization_master (const char *master_path,
2940                         GNUNET_FileNameCallback proc,
2941                         struct GNUNET_FS_Handle *h)
2942 {
2943   char *dn;
2944
2945   dn = get_serialization_file_name (h, master_path, "");
2946   if (dn == NULL)
2947     return;
2948   if (GNUNET_YES ==
2949       GNUNET_DISK_directory_test (dn))
2950     GNUNET_DISK_directory_scan (dn, proc, h);
2951   GNUNET_free (dn); 
2952 }
2953
2954
2955 /**
2956  * Setup a connection to the file-sharing service.
2957  *
2958  * @param cfg configuration to use
2959  * @param client_name unique identifier for this client 
2960  * @param upcb function to call to notify about FS actions
2961  * @param upcb_cls closure for upcb
2962  * @param flags specific attributes for fs-operations
2963  * @param ... list of optional options, terminated with GNUNET_FS_OPTIONS_END
2964  * @return NULL on error
2965  */
2966 struct GNUNET_FS_Handle *
2967 GNUNET_FS_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
2968                  const char *client_name,
2969                  GNUNET_FS_ProgressCallback upcb,
2970                  void *upcb_cls,
2971                  enum GNUNET_FS_Flags flags,
2972                  ...)
2973 {
2974   struct GNUNET_FS_Handle *ret;
2975   enum GNUNET_FS_OPTIONS opt;
2976   va_list ap;
2977
2978   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Handle));
2979   ret->cfg = cfg;
2980   ret->client_name = GNUNET_strdup (client_name);
2981   ret->upcb = upcb;
2982   ret->upcb_cls = upcb_cls;
2983   ret->flags = flags;
2984   ret->max_parallel_downloads = 1;
2985   ret->max_parallel_requests = 1;
2986   ret->avg_block_latency = GNUNET_TIME_UNIT_MINUTES; /* conservative starting point */
2987   va_start (ap, flags);  
2988   while (GNUNET_FS_OPTIONS_END != (opt = va_arg (ap, enum GNUNET_FS_OPTIONS)))
2989     {
2990       switch (opt)
2991         {
2992         case GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM:
2993           ret->max_parallel_downloads = va_arg (ap, unsigned int);
2994           break;
2995         case GNUNET_FS_OPTIONS_REQUEST_PARALLELISM:
2996           ret->max_parallel_requests = va_arg (ap, unsigned int);
2997           break;
2998         default:
2999           GNUNET_break (0);
3000           GNUNET_free (ret->client_name);
3001           GNUNET_free (ret);
3002           va_end (ap);
3003           return NULL;
3004         }
3005     }
3006   va_end (ap);
3007   if (0 != (GNUNET_FS_FLAGS_PERSISTENCE & flags))
3008     {
3009       deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_PUBLISH,
3010                               &deserialize_publish_file,
3011                               ret);
3012       deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_SEARCH, 
3013                               &deserialize_search_file,
3014                               ret);
3015       deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD, 
3016                               &deserialize_download_file,
3017                               ret);
3018       deserialization_master (GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
3019                               &deserialize_unindex_file,
3020                               ret);
3021     }
3022   return ret;
3023 }
3024
3025
3026 /**
3027  * Close our connection with the file-sharing service.
3028  * The callback given to GNUNET_FS_start will no longer be
3029  * called after this function returns.
3030  *
3031  * @param h handle that was returned from GNUNET_FS_start
3032  */                    
3033 void 
3034 GNUNET_FS_stop (struct GNUNET_FS_Handle *h)
3035 {
3036   while (h->top_head != NULL)
3037     h->top_head->ssf (h->top_head->ssf_cls);
3038   if (h->queue_job != GNUNET_SCHEDULER_NO_TASK)
3039     GNUNET_SCHEDULER_cancel (h->queue_job);
3040   GNUNET_free (h->client_name);
3041   GNUNET_free (h);
3042 }
3043
3044
3045 /* end of fs.c */