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