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