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