Fix for #4553
[oweals/gnunet.git] / src / fs / fs_publish.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2010 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20 /**
21  * @file fs/fs_publish.c
22  * @brief publish a file or directory in GNUnet
23  * @see https://gnunet.org/encoding
24  * @author Krista Bennett
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_signatures.h"
30 #include "gnunet_util_lib.h"
31 #include "gnunet_fs_service.h"
32 #include "fs_api.h"
33 #include "fs_tree.h"
34
35
36 /**
37  * Fill in all of the generic fields for
38  * a publish event and call the callback.
39  *
40  * @param pi structure to fill in
41  * @param pc overall publishing context
42  * @param p file information for the file being published
43  * @param offset where in the file are we so far
44  * @return value returned from callback
45  */
46 void *
47 GNUNET_FS_publish_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
48                                 struct GNUNET_FS_PublishContext *pc,
49                                 const struct GNUNET_FS_FileInformation *p,
50                                 uint64_t offset)
51 {
52   pi->value.publish.pc = pc;
53   pi->value.publish.fi = p;
54   pi->value.publish.cctx = p->client_info;
55   pi->value.publish.pctx = (NULL == p->dir) ? NULL : p->dir->client_info;
56   pi->value.publish.filename = p->filename;
57   pi->value.publish.size =
58       (GNUNET_YES == p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
59   pi->value.publish.eta =
60       GNUNET_TIME_calculate_eta (p->start_time, offset,
61                                  pi->value.publish.size);
62   pi->value.publish.completed = offset;
63   pi->value.publish.duration =
64       GNUNET_TIME_absolute_get_duration (p->start_time);
65   pi->value.publish.anonymity = p->bo.anonymity_level;
66   pi->fsh = pc->h;
67   return pc->h->upcb (pc->h->upcb_cls, pi);
68 }
69
70
71 /**
72  * Cleanup the publish context, we're done with it.
73  *
74  * @param pc struct to clean up
75  */
76 static void
77 publish_cleanup (struct GNUNET_FS_PublishContext *pc)
78 {
79   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
80               "Cleaning up publish context (done!)\n");
81   if (NULL != pc->fhc)
82   {
83     GNUNET_CRYPTO_hash_file_cancel (pc->fhc);
84     pc->fhc = NULL;
85   }
86   GNUNET_FS_file_information_destroy (pc->fi, NULL, NULL);
87   GNUNET_free_non_null (pc->nid);
88   GNUNET_free_non_null (pc->nuid);
89   GNUNET_free_non_null (pc->serialization);
90   if (NULL != pc->dsh)
91   {
92     GNUNET_DATASTORE_disconnect (pc->dsh, GNUNET_NO);
93     pc->dsh = NULL;
94   }
95   if (NULL != pc->client)
96   {
97     GNUNET_CLIENT_disconnect (pc->client);
98     pc->client = NULL;
99   }
100   GNUNET_assert (NULL == pc->upload_task);
101   GNUNET_free (pc);
102 }
103
104
105 /**
106  * Function called by the datastore API with
107  * the result from the PUT request.
108  *
109  * @param cls the `struct GNUNET_FS_PublishContext *`
110  * @param success #GNUNET_OK on success
111  * @param min_expiration minimum expiration time required for content to be stored
112  * @param msg error message (or NULL)
113  */
114 static void
115 ds_put_cont (void *cls,
116              int success,
117              struct GNUNET_TIME_Absolute min_expiration,
118              const char *msg)
119 {
120   struct GNUNET_FS_PublishContext *pc = cls;
121   struct GNUNET_FS_ProgressInfo pi;
122
123   pc->qre = NULL;
124   if (GNUNET_SYSERR == success)
125   {
126     GNUNET_asprintf (&pc->fi_pos->emsg,
127                      _("Publishing failed: %s"),
128                      msg);
129     pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
130     pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
131     pi.value.publish.specifics.error.message = pc->fi_pos->emsg;
132     pc->fi_pos->client_info =
133         GNUNET_FS_publish_make_status_ (&pi, pc, pc->fi_pos, 0);
134     if ((GNUNET_YES != pc->fi_pos->is_directory) &&
135         (NULL != pc->fi_pos->filename) &&
136         (GNUNET_YES == pc->any_done) &&
137         (GNUNET_YES == pc->fi_pos->data.file.do_index))
138     {
139       /* run unindex to clean up */
140       GNUNET_FS_unindex_start (pc->h,
141                                pc->fi_pos->filename,
142                                NULL);
143     }
144     return;
145   }
146   pc->any_done = GNUNET_YES;
147   GNUNET_assert (NULL == pc->upload_task);
148   pc->upload_task =
149       GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
150                                           &GNUNET_FS_publish_main_, pc);
151 }
152
153
154 /**
155  * Generate the callback that signals clients
156  * that a file (or directory) has been completely
157  * published.
158  *
159  * @param p the completed upload
160  * @param pc context of the publication
161  */
162 static void
163 signal_publish_completion (struct GNUNET_FS_FileInformation *p,
164                            struct GNUNET_FS_PublishContext *pc)
165 {
166   struct GNUNET_FS_ProgressInfo pi;
167
168   pi.status = GNUNET_FS_STATUS_PUBLISH_COMPLETED;
169   pi.value.publish.eta = GNUNET_TIME_UNIT_ZERO;
170   pi.value.publish.specifics.completed.chk_uri = p->chk_uri;
171   pi.value.publish.specifics.completed.sks_uri = p->sks_uri;
172   p->client_info =
173       GNUNET_FS_publish_make_status_ (&pi, pc, p,
174                                       p->data.file.file_size);
175 }
176
177
178 /**
179  * Generate the callback that signals clients
180  * that a file (or directory) has encountered
181  * a problem during publication.
182  *
183  * @param p the upload that had trouble
184  * @param pc context of the publication
185  * @param emsg error message
186  */
187 static void
188 signal_publish_error (struct GNUNET_FS_FileInformation *p,
189                       struct GNUNET_FS_PublishContext *pc,
190                       const char *emsg)
191 {
192   struct GNUNET_FS_ProgressInfo pi;
193
194   p->emsg = GNUNET_strdup (emsg);
195   pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
196   pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
197   pi.value.publish.specifics.error.message = emsg;
198   p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
199   if ((p->is_directory != GNUNET_YES) &&
200       (NULL != p->filename) &&
201       (GNUNET_YES == pc->any_done) &&
202       (p->data.file.do_index == GNUNET_YES))
203   {
204     /* run unindex to clean up */
205     GNUNET_FS_unindex_start (pc->h,
206                              p->filename,
207                              NULL);
208   }
209
210 }
211
212
213 /**
214  * Datastore returns from reservation cancel request.
215  *
216  * @param cls the `struct GNUNET_FS_PublishContext *`
217  * @param success success code (not used)
218  * @param min_expiration minimum expiration time required for content to be stored
219  * @param msg error message (typically NULL, not used)
220  */
221 static void
222 finish_release_reserve (void *cls, int success,
223                         struct GNUNET_TIME_Absolute min_expiration,
224                         const char *msg)
225 {
226   struct GNUNET_FS_PublishContext *pc = cls;
227
228   pc->qre = NULL;
229   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
230               "Releasing reserve done!\n");
231   signal_publish_completion (pc->fi, pc);
232   pc->all_done = GNUNET_YES;
233   GNUNET_FS_publish_sync_ (pc);
234 }
235
236
237 /**
238  * We've finished publishing the SBlock as part of a larger upload.
239  * Check the result and complete the larger upload.
240  *
241  * @param cls the `struct GNUNET_FS_PublishContext *` of the larger upload
242  * @param uri URI of the published SBlock
243  * @param emsg NULL on success, otherwise error message
244  */
245 static void
246 publish_sblocks_cont (void *cls,
247                       const struct GNUNET_FS_Uri *uri,
248                       const char *emsg)
249 {
250   struct GNUNET_FS_PublishContext *pc = cls;
251
252   pc->sks_pc = NULL;
253   if (NULL != emsg)
254   {
255     signal_publish_error (pc->fi, pc, emsg);
256     GNUNET_FS_publish_sync_ (pc);
257     return;
258   }
259   if (NULL != uri)
260   {
261     /* sks publication, remember namespace URI */
262     pc->fi->sks_uri = GNUNET_FS_uri_dup (uri);
263   }
264   GNUNET_assert (pc->qre == NULL);
265   if ((pc->dsh != NULL) && (pc->rid != 0))
266   {
267     pc->qre =
268         GNUNET_DATASTORE_release_reserve (pc->dsh, pc->rid, UINT_MAX, UINT_MAX,
269                                           GNUNET_TIME_UNIT_FOREVER_REL,
270                                           &finish_release_reserve, pc);
271   }
272   else
273   {
274     finish_release_reserve (pc, GNUNET_OK, GNUNET_TIME_UNIT_ZERO_ABS, NULL);
275   }
276 }
277
278
279 /**
280  * We are almost done publishing the structure,
281  * add SBlocks (if needed).
282  *
283  * @param pc overall upload data
284  */
285 static void
286 publish_sblock (struct GNUNET_FS_PublishContext *pc)
287 {
288   if (NULL != pc->ns)
289     pc->sks_pc = GNUNET_FS_publish_sks (pc->h,
290                                         pc->ns,
291                                         pc->nid,
292                                         pc->nuid,
293                                         pc->fi->meta,
294                                         pc->fi->chk_uri,
295                                         &pc->fi->bo,
296                                         pc->options,
297                                         &publish_sblocks_cont, pc);
298   else
299     publish_sblocks_cont (pc, NULL, NULL);
300 }
301
302
303 /**
304  * We've finished publishing a KBlock as part of a larger upload.
305  * Check the result and continue the larger upload.
306  *
307  * @param cls the `struct GNUNET_FS_PublishContext *`
308  *        of the larger upload
309  * @param uri URI of the published blocks
310  * @param emsg NULL on success, otherwise error message
311  */
312 static void
313 publish_kblocks_cont (void *cls,
314                       const struct GNUNET_FS_Uri *uri,
315                       const char *emsg)
316 {
317   struct GNUNET_FS_PublishContext *pc = cls;
318   struct GNUNET_FS_FileInformation *p = pc->fi_pos;
319
320   pc->ksk_pc = NULL;
321   if (NULL != emsg)
322   {
323     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
324                 "Error uploading KSK blocks: %s\n",
325                 emsg);
326     signal_publish_error (p, pc, emsg);
327     GNUNET_FS_file_information_sync_ (p);
328     GNUNET_FS_publish_sync_ (pc);
329     GNUNET_assert (NULL == pc->upload_task);
330     pc->upload_task =
331       GNUNET_SCHEDULER_add_with_priority
332       (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
333        &GNUNET_FS_publish_main_,
334        pc);
335     return;
336   }
337   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
338               "KSK blocks published, moving on to next file\n");
339   if (NULL != p->dir)
340     signal_publish_completion (p, pc);
341   /* move on to next file */
342   if (NULL != p->next)
343     pc->fi_pos = p->next;
344   else
345     pc->fi_pos = p->dir;
346   GNUNET_FS_publish_sync_ (pc);
347   GNUNET_assert (NULL == pc->upload_task);
348   pc->upload_task =
349       GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
350                                           &GNUNET_FS_publish_main_, pc);
351 }
352
353
354 /**
355  * Function called by the tree encoder to obtain
356  * a block of plaintext data (for the lowest level
357  * of the tree).
358  *
359  * @param cls our publishing context
360  * @param offset identifies which block to get
361  * @param max (maximum) number of bytes to get; returning
362  *        fewer will also cause errors
363  * @param buf where to copy the plaintext buffer
364  * @param emsg location to store an error message (on error)
365  * @return number of bytes copied to buf, 0 on error
366  */
367 static size_t
368 block_reader (void *cls,
369               uint64_t offset,
370               size_t max,
371               void *buf,
372               char **emsg)
373 {
374   struct GNUNET_FS_PublishContext *pc = cls;
375   struct GNUNET_FS_FileInformation *p;
376   const char *dd;
377   size_t pt_size;
378
379   p = pc->fi_pos;
380   if (GNUNET_YES == p->is_directory)
381   {
382     pt_size = GNUNET_MIN (max, p->data.dir.dir_size - offset);
383     dd = p->data.dir.dir_data;
384     memcpy (buf, &dd[offset], pt_size);
385   }
386   else
387   {
388     if (UINT64_MAX == offset)
389     {
390       if (&GNUNET_FS_data_reader_file_ == p->data.file.reader)
391       {
392         /* force closing the file to avoid keeping too many files open */
393         p->data.file.reader (p->data.file.reader_cls, offset, 0, NULL, NULL);
394       }
395       return 0;
396     }
397     pt_size = GNUNET_MIN (max, p->data.file.file_size - offset);
398     if (0 == pt_size)
399       return 0;                 /* calling reader with pt_size==0
400                                  * might free buf, so don't! */
401     if (pt_size !=
402         p->data.file.reader (p->data.file.reader_cls, offset, pt_size, buf,
403                              emsg))
404       return 0;
405   }
406   return pt_size;
407 }
408
409
410 /**
411  * The tree encoder has finished processing a
412  * file.   Call it's finish method and deal with
413  * the final result.
414  *
415  * @param cls our publishing context
416  */
417 static void
418 encode_cont (void *cls)
419 {
420   struct GNUNET_FS_PublishContext *pc = cls;
421   struct GNUNET_FS_FileInformation *p;
422   struct GNUNET_FS_ProgressInfo pi;
423   char *emsg;
424   uint64_t flen;
425
426   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
427               "Finished with tree encoder\n");
428   p = pc->fi_pos;
429   p->chk_uri = GNUNET_FS_tree_encoder_get_uri (p->te);
430   GNUNET_FS_file_information_sync_ (p);
431   GNUNET_FS_tree_encoder_finish (p->te, &emsg);
432   p->te = NULL;
433   if (NULL != emsg)
434   {
435     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
436                 "Error during tree walk: %s\n",
437                 emsg);
438     GNUNET_asprintf (&p->emsg,
439                      _("Publishing failed: %s"),
440                      emsg);
441     GNUNET_free (emsg);
442     pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
443     pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
444     pi.value.publish.specifics.error.message = p->emsg;
445     p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
446   }
447   else
448   {
449     /* final progress event */
450     GNUNET_assert (NULL != p->chk_uri);
451     flen = GNUNET_FS_uri_chk_get_file_size (p->chk_uri);
452     pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
453     pi.value.publish.specifics.progress.data = NULL;
454     pi.value.publish.specifics.progress.offset = flen;
455     pi.value.publish.specifics.progress.data_len = 0;
456     pi.value.publish.specifics.progress.depth = GNUNET_FS_compute_depth (flen);
457     p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, flen);
458   }
459   /* continue with main */  /* continue with main */
460   GNUNET_assert (NULL == pc->upload_task);
461   pc->upload_task =
462       GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
463                                           &GNUNET_FS_publish_main_, pc);
464 }
465
466
467 /**
468  * Function called asking for the current (encoded)
469  * block to be processed.  After processing the
470  * client should either call #GNUNET_FS_tree_encoder_next
471  * or (on error) #GNUNET_FS_tree_encoder_finish.
472  *
473  * @param cls closure
474  * @param chk content hash key for the block
475  * @param offset offset of the block in the file
476  * @param depth depth of the block in the file, 0 for DBLOCK
477  * @param type type of the block (IBLOCK or DBLOCK)
478  * @param block the (encrypted) block
479  * @param block_size size of @a block (in bytes)
480  */
481 static void
482 block_proc (void *cls,
483             const struct ContentHashKey *chk,
484             uint64_t offset,
485             unsigned int depth,
486             enum GNUNET_BLOCK_Type type,
487             const void *block,
488             uint16_t block_size)
489 {
490   struct GNUNET_FS_PublishContext *pc = cls;
491   struct GNUNET_FS_FileInformation *p;
492   struct OnDemandBlock odb;
493
494   p = pc->fi_pos;
495   if (NULL == pc->dsh)
496   {
497     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Waiting for datastore connection\n");
498     GNUNET_assert (NULL == pc->upload_task);
499     pc->upload_task =
500         GNUNET_SCHEDULER_add_with_priority
501         (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, &GNUNET_FS_publish_main_, pc);
502     return;
503   }
504
505   if ( (GNUNET_YES != p->is_directory) &&
506        (GNUNET_YES == p->data.file.do_index) &&
507        (GNUNET_BLOCK_TYPE_FS_DBLOCK == type) )
508   {
509     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
510                 "Indexing block `%s' for offset %llu with index size %u\n",
511                 GNUNET_h2s (&chk->query),
512                 (unsigned long long) offset,
513                 (unsigned int) sizeof (struct OnDemandBlock));
514     odb.offset = GNUNET_htonll (offset);
515     odb.file_id = p->data.file.file_id;
516     GNUNET_assert (pc->qre == NULL);
517     pc->qre =
518         GNUNET_DATASTORE_put (pc->dsh,
519                               (p->is_directory == GNUNET_YES) ? 0 : pc->rid,
520                               &chk->query,
521                               sizeof (struct OnDemandBlock),
522                               &odb,
523                               GNUNET_BLOCK_TYPE_FS_ONDEMAND,
524                               p->bo.content_priority,
525                               p->bo.anonymity_level,
526                               p->bo.replication_level,
527                               p->bo.expiration_time,
528                               -2, 1,
529                               GNUNET_CONSTANTS_SERVICE_TIMEOUT,
530                               &ds_put_cont, pc);
531     return;
532   }
533   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
534               "Publishing block `%s' for offset %llu with size %u\n",
535               GNUNET_h2s (&chk->query),
536               (unsigned long long) offset,
537               (unsigned int) block_size);
538   GNUNET_assert (pc->qre == NULL);
539   pc->qre =
540       GNUNET_DATASTORE_put (pc->dsh, (p->is_directory == GNUNET_YES) ? 0 : pc->rid,
541                             &chk->query,
542                             block_size,
543                             block,
544                             type,
545                             p->bo.content_priority,
546                             p->bo.anonymity_level,
547                             p->bo.replication_level,
548                             p->bo.expiration_time,
549                             -2, 1,
550                             GNUNET_CONSTANTS_SERVICE_TIMEOUT,
551                             &ds_put_cont,
552                             pc);
553 }
554
555
556 /**
557  * Function called with information about our
558  * progress in computing the tree encoding.
559  *
560  * @param cls closure
561  * @param offset where are we in the file
562  * @param pt_block plaintext of the currently processed block
563  * @param pt_size size of @a pt_block
564  * @param depth depth of the block in the tree, 0 for DBLOCK
565  */
566 static void
567 progress_proc (void *cls, uint64_t offset,
568                const void *pt_block,
569                size_t pt_size,
570                unsigned int depth)
571 {
572   struct GNUNET_FS_PublishContext *pc = cls;
573   struct GNUNET_FS_FileInformation *p;
574   struct GNUNET_FS_FileInformation *par;
575   struct GNUNET_FS_ProgressInfo pi;
576
577   p = pc->fi_pos;
578   pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
579   pi.value.publish.specifics.progress.data = pt_block;
580   pi.value.publish.specifics.progress.offset = offset;
581   pi.value.publish.specifics.progress.data_len = pt_size;
582   pi.value.publish.specifics.progress.depth = depth;
583   p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, offset);
584   if ( (0 != depth) ||
585        (GNUNET_YES == p->is_directory) )
586     return;
587   while (NULL != (par = p->dir))
588   {
589     p = par;
590     GNUNET_assert (GNUNET_YES == par->is_directory);
591     p->data.dir.contents_completed += pt_size;
592     pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY;
593     pi.value.publish.specifics.progress_directory.completed = p->data.dir.contents_completed;
594     pi.value.publish.specifics.progress_directory.total = p->data.dir.contents_size;
595     pi.value.publish.specifics.progress_directory.eta = GNUNET_TIME_calculate_eta (p->start_time,
596                                                                                    p->data.dir.contents_completed,
597                                                                                    p->data.dir.contents_size);
598     p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
599
600   }
601 }
602
603
604 /**
605  * We are uploading a file or directory; load (if necessary) the next
606  * block into memory, encrypt it and send it to the FS service.  Then
607  * continue with the main task.
608  *
609  * @param pc overall upload data
610  */
611 static void
612 publish_content (struct GNUNET_FS_PublishContext *pc)
613 {
614   struct GNUNET_FS_FileInformation *p;
615   char *emsg;
616   struct GNUNET_FS_DirectoryBuilder *db;
617   struct GNUNET_FS_FileInformation *dirpos;
618   void *raw_data;
619   uint64_t size;
620
621   p = pc->fi_pos;
622   GNUNET_assert (NULL != p);
623   if (NULL == p->te)
624   {
625     if (GNUNET_YES == p->is_directory)
626     {
627       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating directory\n");
628       db = GNUNET_FS_directory_builder_create (p->meta);
629       dirpos = p->data.dir.entries;
630       while (NULL != dirpos)
631       {
632         if (GNUNET_YES == dirpos->is_directory)
633         {
634           raw_data = dirpos->data.dir.dir_data;
635           dirpos->data.dir.dir_data = NULL;
636         }
637         else
638         {
639           raw_data = NULL;
640           if ((dirpos->data.file.file_size < MAX_INLINE_SIZE) &&
641               (dirpos->data.file.file_size > 0))
642           {
643             raw_data = GNUNET_malloc (dirpos->data.file.file_size);
644             emsg = NULL;
645             if (dirpos->data.file.file_size !=
646                 dirpos->data.file.reader (dirpos->data.file.reader_cls, 0,
647                                           dirpos->data.file.file_size, raw_data,
648                                           &emsg))
649             {
650               GNUNET_free_non_null (emsg);
651               GNUNET_free (raw_data);
652               raw_data = NULL;
653             }
654             dirpos->data.file.reader (dirpos->data.file.reader_cls, UINT64_MAX, 0, 0, NULL);
655           }
656         }
657         GNUNET_FS_directory_builder_add (db, dirpos->chk_uri, dirpos->meta,
658                                          raw_data);
659         GNUNET_free_non_null (raw_data);
660         dirpos = dirpos->next;
661       }
662       GNUNET_free_non_null (p->data.dir.dir_data);
663       p->data.dir.dir_data = NULL;
664       p->data.dir.dir_size = 0;
665       GNUNET_FS_directory_builder_finish (db, &p->data.dir.dir_size,
666                                           &p->data.dir.dir_data);
667       GNUNET_FS_file_information_sync_ (p);
668     }
669     size = (GNUNET_YES == p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
670     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
671                 "Creating tree encoder\n");
672     p->te =
673         GNUNET_FS_tree_encoder_create (pc->h, size, pc, &block_reader,
674                                        &block_proc, &progress_proc,
675                                        &encode_cont);
676
677   }
678   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
679               "Processing next block from tree\n");
680   GNUNET_FS_tree_encoder_next (p->te);
681 }
682
683
684 /**
685  * Process the response (or lack thereof) from
686  * the "fs" service to our 'start index' request.
687  *
688  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
689  * @param msg the response we got
690  */
691 static void
692 process_index_start_response (void *cls,
693                               const struct GNUNET_MessageHeader *msg)
694 {
695   struct GNUNET_FS_PublishContext *pc = cls;
696   struct GNUNET_FS_FileInformation *p;
697   const char *emsg;
698   uint16_t msize;
699
700   GNUNET_CLIENT_disconnect (pc->client);
701   pc->client = NULL;
702   p = pc->fi_pos;
703   if (NULL == msg)
704   {
705     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
706                 _("Can not index file `%s': %s.  Will try to insert instead.\n"),
707                 p->filename,
708                 _("timeout on index-start request to `fs' service"));
709     p->data.file.do_index = GNUNET_NO;
710     GNUNET_FS_file_information_sync_ (p);
711     publish_content (pc);
712     return;
713   }
714   if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_INDEX_START_OK)
715   {
716     msize = ntohs (msg->size);
717     emsg = (const char *) &msg[1];
718     if ((msize <= sizeof (struct GNUNET_MessageHeader)) ||
719         (emsg[msize - sizeof (struct GNUNET_MessageHeader) - 1] != '\0'))
720       emsg = gettext_noop ("unknown error");
721     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
722                 _
723                 ("Can not index file `%s': %s.  Will try to insert instead.\n"),
724                 p->filename, gettext (emsg));
725     p->data.file.do_index = GNUNET_NO;
726     GNUNET_FS_file_information_sync_ (p);
727     publish_content (pc);
728     return;
729   }
730   p->data.file.index_start_confirmed = GNUNET_YES;
731   /* success! continue with indexing */
732   GNUNET_FS_file_information_sync_ (p);
733   publish_content (pc);
734 }
735
736
737 /**
738  * Function called once the hash computation over an
739  * indexed file has completed.
740  *
741  * @param cls closure, our publishing context
742  * @param res resulting hash, NULL on error
743  */
744 static void
745 hash_for_index_cb (void *cls,
746                    const struct GNUNET_HashCode *res)
747 {
748   struct GNUNET_FS_PublishContext *pc = cls;
749   struct GNUNET_FS_FileInformation *p;
750   struct IndexStartMessage *ism;
751   size_t slen;
752   struct GNUNET_CLIENT_Connection *client;
753   uint64_t dev;
754   uint64_t ino;
755   char *fn;
756
757   pc->fhc = NULL;
758   p = pc->fi_pos;
759   if (NULL == res)
760   {
761     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
762                 _("Can not index file `%s': %s.  Will try to insert instead.\n"),
763                 p->filename,
764                 _("failed to compute hash"));
765     p->data.file.do_index = GNUNET_NO;
766     GNUNET_FS_file_information_sync_ (p);
767     publish_content (pc);
768     return;
769   }
770   if (GNUNET_YES == p->data.file.index_start_confirmed)
771   {
772     publish_content (pc);
773     return;
774   }
775   fn = GNUNET_STRINGS_filename_expand (p->filename);
776   GNUNET_assert (fn != NULL);
777   slen = strlen (fn) + 1;
778   if (slen >=
779       GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct IndexStartMessage))
780   {
781     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
782                 _
783                 ("Can not index file `%s': %s.  Will try to insert instead.\n"),
784                 fn, _("filename too long"));
785     GNUNET_free (fn);
786     p->data.file.do_index = GNUNET_NO;
787     GNUNET_FS_file_information_sync_ (p);
788     publish_content (pc);
789     return;
790   }
791   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hash of indexed file `%s' is `%s'\n",
792               p->filename, GNUNET_h2s (res));
793   if (0 != (pc->options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
794   {
795     p->data.file.file_id = *res;
796     p->data.file.have_hash = GNUNET_YES;
797     p->data.file.index_start_confirmed = GNUNET_YES;
798     GNUNET_FS_file_information_sync_ (p);
799     publish_content (pc);
800     GNUNET_free (fn);
801     return;
802   }
803   client = GNUNET_CLIENT_connect ("fs", pc->h->cfg);
804   if (NULL == client)
805   {
806     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
807                 _("Can not index file `%s': %s.  Will try to insert instead.\n"),
808                 p->filename,
809                 _("could not connect to `fs' service"));
810     p->data.file.do_index = GNUNET_NO;
811     publish_content (pc);
812     GNUNET_free (fn);
813     return;
814   }
815   if (p->data.file.have_hash != GNUNET_YES)
816   {
817     p->data.file.file_id = *res;
818     p->data.file.have_hash = GNUNET_YES;
819     GNUNET_FS_file_information_sync_ (p);
820   }
821   ism = GNUNET_malloc (sizeof (struct IndexStartMessage) + slen);
822   ism->header.size = htons (sizeof (struct IndexStartMessage) + slen);
823   ism->header.type = htons (GNUNET_MESSAGE_TYPE_FS_INDEX_START);
824   if (GNUNET_OK == GNUNET_DISK_file_get_identifiers (p->filename, &dev, &ino))
825   {
826     ism->device = GNUNET_htonll (dev);
827     ism->inode = GNUNET_htonll (ino);
828   }
829   else
830   {
831     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
832                 _("Failed to get file identifiers for `%s'\n"), p->filename);
833   }
834   ism->file_id = *res;
835   memcpy (&ism[1], fn, slen);
836   GNUNET_free (fn);
837   pc->client = client;
838   GNUNET_break (GNUNET_YES ==
839                 GNUNET_CLIENT_transmit_and_get_response (client, &ism->header,
840                                                          GNUNET_TIME_UNIT_FOREVER_REL,
841                                                          GNUNET_YES,
842                                                          &process_index_start_response,
843                                                          pc));
844   GNUNET_free (ism);
845 }
846
847
848 /**
849  * We've computed the CHK/LOC URI, now publish the KSKs (if applicable).
850  *
851  * @param pc publishing context to do this for
852  */
853 static void
854 publish_kblocks (struct GNUNET_FS_PublishContext *pc)
855 {
856   struct GNUNET_FS_FileInformation *p;
857
858   p = pc->fi_pos;
859   /* upload of "p" complete, publish KBlocks! */
860   if (NULL != p->keywords)
861   {
862     pc->ksk_pc = GNUNET_FS_publish_ksk (pc->h,
863                                         p->keywords,
864                                         p->meta,
865                                         p->chk_uri,
866                                         &p->bo,
867                                         pc->options,
868                                         &publish_kblocks_cont, pc);
869   }
870   else
871   {
872     publish_kblocks_cont (pc, p->chk_uri, NULL);
873   }
874 }
875
876
877 /**
878  * Process the response (or lack thereof) from
879  * the "fs" service to our LOC sign request.
880  *
881  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
882  * @param msg the response we got
883  */
884 static void
885 process_signature_response (void *cls,
886                             const struct GNUNET_MessageHeader *msg)
887 {
888   struct GNUNET_FS_PublishContext *pc = cls;
889   const struct ResponseLocSignatureMessage *sig;
890   struct GNUNET_FS_FileInformation *p;
891
892   p = pc->fi_pos;
893   if (NULL == msg)
894   {
895     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
896                 _("Can not create LOC URI. Will continue with CHK instead.\n"));
897     publish_kblocks (pc);
898     return;
899   }
900   if (sizeof (struct ResponseLocSignatureMessage) !=
901       ntohs (msg->size))
902   {
903     GNUNET_break (0);
904     publish_kblocks (pc);
905     return;
906   }
907   sig = (const struct ResponseLocSignatureMessage *) msg;
908   p->chk_uri->type = GNUNET_FS_URI_LOC;
909   /* p->data.loc.fi kept from CHK before */
910   p->chk_uri->data.loc.peer = sig->peer;
911   p->chk_uri->data.loc.expirationTime = GNUNET_TIME_absolute_ntoh (sig->expiration_time);
912   p->chk_uri->data.loc.contentSignature = sig->signature;
913   GNUNET_FS_file_information_sync_ (p);
914   GNUNET_FS_publish_sync_ (pc);
915   publish_kblocks (pc);
916 }
917
918
919 /**
920  * We're publishing without anonymity. Contact the FS service
921  * to create a signed LOC URI for further processing, then
922  * continue with KSKs.
923  *
924  * @param pc the publishing context do to this for
925  */
926 static void
927 create_loc_uri (struct GNUNET_FS_PublishContext *pc)
928 {
929   struct RequestLocSignatureMessage req;
930   struct GNUNET_FS_FileInformation *p;
931
932   if (NULL == pc->client)
933     pc->client = GNUNET_CLIENT_connect ("fs", pc->h->cfg);
934   if (NULL == pc->client)
935   {
936     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
937                 _("Can not create LOC URI. Will continue with CHK instead.\n"));
938     publish_kblocks (pc);
939     return;
940   }
941   p = pc->fi_pos;
942   req.header.size = htons (sizeof (struct RequestLocSignatureMessage));
943   req.header.type = htons (GNUNET_MESSAGE_TYPE_FS_REQUEST_LOC_SIGN);
944   req.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT);
945   req.expiration_time = GNUNET_TIME_absolute_hton (p->bo.expiration_time);
946   req.chk = p->chk_uri->data.chk.chk;
947   req.file_length = GNUNET_htonll (p->chk_uri->data.chk.file_length);
948   GNUNET_break (GNUNET_YES ==
949                 GNUNET_CLIENT_transmit_and_get_response (pc->client,
950                                                          &req.header,
951                                                          GNUNET_TIME_UNIT_FOREVER_REL,
952                                                          GNUNET_YES,
953                                                          &process_signature_response,
954                                                          pc));
955 }
956
957
958 /**
959  * Main function that performs the upload.
960  *
961  * @param cls `struct GNUNET_FS_PublishContext *` identifies the upload
962  */
963 void
964 GNUNET_FS_publish_main_ (void *cls)
965 {
966   struct GNUNET_FS_PublishContext *pc = cls;
967   struct GNUNET_FS_ProgressInfo pi;
968   struct GNUNET_FS_FileInformation *p;
969   char *fn;
970
971   pc->upload_task = NULL;
972   p = pc->fi_pos;
973   if (NULL == p)
974   {
975     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
976                 "Publishing complete, now publishing SKS and KSK blocks.\n");
977     /* upload of entire hierarchy complete,
978      * publish namespace entries */
979     GNUNET_FS_publish_sync_ (pc);
980     publish_sblock (pc);
981     return;
982   }
983   /* find starting position */
984   while ( (GNUNET_YES == p->is_directory) &&
985           (NULL != p->data.dir.entries) &&
986           (NULL == p->emsg) &&
987           (NULL == p->data.dir.entries->chk_uri) )
988   {
989     p = p->data.dir.entries;
990     pc->fi_pos = p;
991     GNUNET_FS_publish_sync_ (pc);
992   }
993   /* abort on error */
994   if (NULL != p->emsg)
995   {
996     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
997                 "Error uploading: %s\n",
998                 p->emsg);
999     /* error with current file, abort all
1000      * related files as well! */
1001     while (NULL != p->dir)
1002     {
1003       fn = GNUNET_CONTAINER_meta_data_get_by_type (p->meta,
1004                                                    EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
1005       p = p->dir;
1006       if (fn != NULL)
1007       {
1008         GNUNET_asprintf (&p->emsg,
1009                          _("Recursive upload failed at `%s': %s"),
1010                          fn,
1011                          p->emsg);
1012         GNUNET_free (fn);
1013       }
1014       else
1015       {
1016         GNUNET_asprintf (&p->emsg,
1017                          _("Recursive upload failed: %s"),
1018                          p->emsg);
1019       }
1020       pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
1021       pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
1022       pi.value.publish.specifics.error.message = p->emsg;
1023       p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
1024     }
1025     pc->all_done = GNUNET_YES;
1026     GNUNET_FS_publish_sync_ (pc);
1027     return;
1028   }
1029   /* handle completion */
1030   if (NULL != p->chk_uri)
1031   {
1032     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1033                 "File upload complete, now publishing KSK blocks.\n");
1034     GNUNET_FS_publish_sync_ (pc);
1035
1036     if ( (0 == p->bo.anonymity_level) &&
1037          (GNUNET_YES !=
1038           GNUNET_FS_uri_test_loc (p->chk_uri)) )
1039     {
1040       /* zero anonymity, box CHK URI in LOC URI */
1041       create_loc_uri (pc);
1042     }
1043     else
1044     {
1045       publish_kblocks (pc);
1046     }
1047     return;
1048   }
1049   if ((GNUNET_YES != p->is_directory) && (p->data.file.do_index))
1050   {
1051     if (NULL == p->filename)
1052     {
1053       p->data.file.do_index = GNUNET_NO;
1054       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1055                   _("Can not index file `%s': %s.  Will try to insert instead.\n"),
1056                   "<no-name>",
1057                   _("needs to be an actual file"));
1058       GNUNET_FS_file_information_sync_ (p);
1059       publish_content (pc);
1060       return;
1061     }
1062     if (p->data.file.have_hash)
1063     {
1064       hash_for_index_cb (pc, &p->data.file.file_id);
1065     }
1066     else
1067     {
1068       p->start_time = GNUNET_TIME_absolute_get ();
1069       pc->fhc =
1070           GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE, p->filename,
1071                                    HASHING_BLOCKSIZE, &hash_for_index_cb, pc);
1072     }
1073     return;
1074   }
1075   publish_content (pc);
1076 }
1077
1078
1079 /**
1080  * Signal the FS's progress function that we are starting
1081  * an upload.
1082  *
1083  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
1084  * @param fi the entry in the publish-structure
1085  * @param length length of the file or directory
1086  * @param meta metadata for the file or directory (can be modified)
1087  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1088  * @param bo block options
1089  * @param do_index should we index?
1090  * @param client_info pointer to client context set upon creation (can be modified)
1091  * @return #GNUNET_OK to continue (always)
1092  */
1093 static int
1094 fip_signal_start (void *cls,
1095                   struct GNUNET_FS_FileInformation *fi,
1096                   uint64_t length,
1097                   struct GNUNET_CONTAINER_MetaData *meta,
1098                   struct GNUNET_FS_Uri **uri,
1099                   struct GNUNET_FS_BlockOptions *bo,
1100                   int *do_index,
1101                   void **client_info)
1102 {
1103   struct GNUNET_FS_PublishContext *pc = cls;
1104   struct GNUNET_FS_ProgressInfo pi;
1105   unsigned int kc;
1106   uint64_t left;
1107
1108   if (GNUNET_YES == pc->skip_next_fi_callback)
1109   {
1110     pc->skip_next_fi_callback = GNUNET_NO;
1111     return GNUNET_OK;
1112   }
1113   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1114               "Starting publish operation\n");
1115   if (*do_index)
1116   {
1117     /* space for on-demand blocks */
1118     pc->reserve_space +=
1119         ((length + DBLOCK_SIZE -
1120           1) / DBLOCK_SIZE) * sizeof (struct OnDemandBlock);
1121   }
1122   else
1123   {
1124     /* space for DBlocks */
1125     pc->reserve_space += length;
1126   }
1127   /* entries for IBlocks and DBlocks, space for IBlocks */
1128   left = length;
1129   while (1)
1130   {
1131     left = (left + DBLOCK_SIZE - 1) / DBLOCK_SIZE;
1132     pc->reserve_entries += left;
1133     if (left <= 1)
1134       break;
1135     left = left * sizeof (struct ContentHashKey);
1136     pc->reserve_space += left;
1137   }
1138   pc->reserve_entries++;
1139   /* entries and space for keywords */
1140   if (NULL != *uri)
1141   {
1142     kc = GNUNET_FS_uri_ksk_get_keyword_count (*uri);
1143     pc->reserve_entries += kc;
1144     pc->reserve_space += GNUNET_SERVER_MAX_MESSAGE_SIZE * kc;
1145   }
1146   pi.status = GNUNET_FS_STATUS_PUBLISH_START;
1147   *client_info = GNUNET_FS_publish_make_status_ (&pi, pc, fi, 0);
1148   GNUNET_FS_file_information_sync_ (fi);
1149   if ((fi->is_directory) && (fi->dir != NULL))
1150   {
1151     /* We are a directory, and we are not top-level; process entries in directory */
1152     pc->skip_next_fi_callback = GNUNET_YES;
1153     GNUNET_FS_file_information_inspect (fi, &fip_signal_start, pc);
1154   }
1155   return GNUNET_OK;
1156 }
1157
1158
1159 /**
1160  * Actually signal the FS's progress function that we are suspending
1161  * an upload.
1162  *
1163  * @param fi the entry in the publish-structure
1164  * @param pc the publish context of which a file is being suspended
1165  */
1166 static void
1167 suspend_operation (struct GNUNET_FS_FileInformation *fi,
1168                    struct GNUNET_FS_PublishContext *pc)
1169 {
1170   struct GNUNET_FS_ProgressInfo pi;
1171   uint64_t off;
1172
1173   if (NULL != pc->ksk_pc)
1174   {
1175     GNUNET_FS_publish_ksk_cancel (pc->ksk_pc);
1176     pc->ksk_pc = NULL;
1177   }
1178   if (NULL != pc->sks_pc)
1179   {
1180     GNUNET_FS_publish_sks_cancel (pc->sks_pc);
1181     pc->sks_pc = NULL;
1182   }
1183   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1184               "Suspending publish operation\n");
1185   GNUNET_free_non_null (fi->serialization);
1186   fi->serialization = NULL;
1187   off = (NULL == fi->chk_uri) ? 0 : (GNUNET_YES == fi->is_directory) ? fi->data.dir.dir_size : fi->data.file.file_size;
1188   pi.status = GNUNET_FS_STATUS_PUBLISH_SUSPEND;
1189   GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, fi, off));
1190   if (NULL != pc->qre)
1191   {
1192     GNUNET_DATASTORE_cancel (pc->qre);
1193     pc->qre = NULL;
1194   }
1195   if (NULL != pc->dsh)
1196   {
1197     GNUNET_DATASTORE_disconnect (pc->dsh, GNUNET_NO);
1198     pc->dsh = NULL;
1199   }
1200   pc->rid = 0;
1201 }
1202
1203
1204 /**
1205  * Signal the FS's progress function that we are suspending
1206  * an upload.  Performs the recursion.
1207  *
1208  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
1209  * @param fi the entry in the publish-structure
1210  * @param length length of the file or directory
1211  * @param meta metadata for the file or directory (can be modified)
1212  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1213  * @param bo block options
1214  * @param do_index should we index?
1215  * @param client_info pointer to client context set upon creation (can be modified)
1216  * @return #GNUNET_OK to continue (always)
1217  */
1218 static int
1219 fip_signal_suspend (void *cls,
1220                     struct GNUNET_FS_FileInformation *fi,
1221                     uint64_t length,
1222                     struct GNUNET_CONTAINER_MetaData *meta,
1223                     struct GNUNET_FS_Uri **uri,
1224                     struct GNUNET_FS_BlockOptions *bo,
1225                     int *do_index,
1226                     void **client_info)
1227 {
1228   struct GNUNET_FS_PublishContext *pc = cls;
1229
1230   if (GNUNET_YES == pc->skip_next_fi_callback)
1231   {
1232     pc->skip_next_fi_callback = GNUNET_NO;
1233     return GNUNET_OK;
1234   }
1235   if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta))
1236   {
1237     /* process entries in directory */
1238     pc->skip_next_fi_callback = GNUNET_YES;
1239     GNUNET_FS_file_information_inspect (fi, &fip_signal_suspend, pc);
1240   }
1241   suspend_operation (fi, pc);
1242   *client_info = NULL;
1243   return GNUNET_OK;
1244 }
1245
1246
1247 /**
1248  * Create SUSPEND event for the given publish operation
1249  * and then clean up our state (without stop signal).
1250  *
1251  * @param cls the `struct GNUNET_FS_PublishContext` to signal for
1252  */
1253 void
1254 GNUNET_FS_publish_signal_suspend_ (void *cls)
1255 {
1256   struct GNUNET_FS_PublishContext *pc = cls;
1257
1258   if (NULL != pc->upload_task)
1259   {
1260     GNUNET_SCHEDULER_cancel (pc->upload_task);
1261     pc->upload_task = NULL;
1262   }
1263   pc->skip_next_fi_callback = GNUNET_YES;
1264   GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_suspend, pc);
1265   suspend_operation (pc->fi, pc);
1266   GNUNET_FS_end_top (pc->h, pc->top);
1267   pc->top = NULL;
1268   publish_cleanup (pc);
1269 }
1270
1271
1272 /**
1273  * We have gotten a reply for our space reservation request.
1274  * Either fail (insufficient space) or start publishing for good.
1275  *
1276  * @param cls the `struct GNUNET_FS_PublishContext *`
1277  * @param success positive reservation ID on success
1278  * @param min_expiration minimum expiration time required for content to be stored
1279  * @param msg error message on error, otherwise NULL
1280  */
1281 static void
1282 finish_reserve (void *cls,
1283                 int success,
1284                 struct GNUNET_TIME_Absolute min_expiration,
1285                 const char *msg)
1286 {
1287   struct GNUNET_FS_PublishContext *pc = cls;
1288
1289   pc->qre = NULL;
1290   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1291               "Reservation complete (%d)!\n",
1292               success);
1293   if ((msg != NULL) || (success <= 0))
1294   {
1295     GNUNET_asprintf (&pc->fi->emsg,
1296                      _("Datastore failure: %s"),
1297                      msg);
1298     signal_publish_error (pc->fi, pc, pc->fi->emsg);
1299     return;
1300   }
1301   pc->rid = success;
1302   GNUNET_assert (NULL == pc->upload_task);
1303   pc->upload_task =
1304       GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
1305                                           &GNUNET_FS_publish_main_, pc);
1306 }
1307
1308
1309 /**
1310  * Calculate the total size of all of the files in the directory structure.
1311  *
1312  * @param fi file structure to traverse
1313  */
1314 static uint64_t
1315 compute_contents_size (struct GNUNET_FS_FileInformation *fi)
1316 {
1317   struct GNUNET_FS_FileInformation *ent;
1318
1319   if (GNUNET_YES != fi->is_directory)
1320     return fi->data.file.file_size;
1321   fi->data.dir.contents_size = 0;
1322   for (ent = fi->data.dir.entries; NULL != ent; ent = ent->next)
1323     fi->data.dir.contents_size += compute_contents_size (ent);
1324   return fi->data.dir.contents_size;
1325 }
1326
1327
1328 /**
1329  * Publish a file or directory.
1330  *
1331  * @param h handle to the file sharing subsystem
1332  * @param fi information about the file or directory structure to publish
1333  * @param ns namespace to publish the file in, NULL for no namespace
1334  * @param nid identifier to use for the publishd content in the namespace
1335  *        (can be NULL, must be NULL if namespace is NULL)
1336  * @param nuid update-identifier that will be used for future updates
1337  *        (can be NULL, must be NULL if namespace or nid is NULL)
1338  * @param options options for the publication
1339  * @return context that can be used to control the publish operation
1340  */
1341 struct GNUNET_FS_PublishContext *
1342 GNUNET_FS_publish_start (struct GNUNET_FS_Handle *h,
1343                          struct GNUNET_FS_FileInformation *fi,
1344                          const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns,
1345                          const char *nid,
1346                          const char *nuid,
1347                          enum GNUNET_FS_PublishOptions options)
1348 {
1349   struct GNUNET_FS_PublishContext *ret;
1350   struct GNUNET_DATASTORE_Handle *dsh;
1351
1352   GNUNET_assert (NULL != h);
1353   compute_contents_size (fi);
1354   if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1355   {
1356     dsh = GNUNET_DATASTORE_connect (h->cfg);
1357     if (NULL == dsh)
1358       return NULL;
1359   }
1360   else
1361   {
1362     dsh = NULL;
1363   }
1364   ret = GNUNET_new (struct GNUNET_FS_PublishContext);
1365   ret->dsh = dsh;
1366   ret->h = h;
1367   ret->fi = fi;
1368   if (NULL != ns)
1369   {
1370     ret->ns = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey);
1371     *ret->ns = *ns;
1372     GNUNET_assert (NULL != nid);
1373     ret->nid = GNUNET_strdup (nid);
1374     if (NULL != nuid)
1375       ret->nuid = GNUNET_strdup (nuid);
1376   }
1377   ret->options = options;
1378   /* signal start */
1379   GNUNET_FS_file_information_inspect (ret->fi, &fip_signal_start, ret);
1380   ret->fi_pos = ret->fi;
1381   ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_publish_signal_suspend_, ret);
1382   GNUNET_FS_publish_sync_ (ret);
1383   if (NULL != ret->dsh)
1384   {
1385     GNUNET_assert (NULL == ret->qre);
1386     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1387                 _("Reserving space for %u entries and %llu bytes for publication\n"),
1388                 (unsigned int) ret->reserve_entries,
1389                 (unsigned long long) ret->reserve_space);
1390     ret->qre =
1391         GNUNET_DATASTORE_reserve (ret->dsh, ret->reserve_space,
1392                                   ret->reserve_entries,
1393                                   &finish_reserve,
1394                                   ret);
1395   }
1396   else
1397   {
1398     GNUNET_assert (NULL == ret->upload_task);
1399     ret->upload_task =
1400         GNUNET_SCHEDULER_add_with_priority
1401         (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, &GNUNET_FS_publish_main_, ret);
1402   }
1403   return ret;
1404 }
1405
1406
1407 /**
1408  * Signal the FS's progress function that we are stopping
1409  * an upload.
1410  *
1411  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
1412  * @param fi the entry in the publish-structure
1413  * @param length length of the file or directory
1414  * @param meta metadata for the file or directory (can be modified)
1415  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1416  * @param bo block options (can be modified)
1417  * @param do_index should we index?
1418  * @param client_info pointer to client context set upon creation (can be modified)
1419  * @return #GNUNET_OK to continue (always)
1420  */
1421 static int
1422 fip_signal_stop (void *cls,
1423                  struct GNUNET_FS_FileInformation *fi,
1424                  uint64_t length,
1425                  struct GNUNET_CONTAINER_MetaData *meta,
1426                  struct GNUNET_FS_Uri **uri,
1427                  struct GNUNET_FS_BlockOptions *bo,
1428                  int *do_index, void **client_info)
1429 {
1430   struct GNUNET_FS_PublishContext *pc = cls;
1431   struct GNUNET_FS_ProgressInfo pi;
1432   uint64_t off;
1433
1434   if (GNUNET_YES == pc->skip_next_fi_callback)
1435   {
1436     pc->skip_next_fi_callback = GNUNET_NO;
1437     return GNUNET_OK;
1438   }
1439   if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta))
1440   {
1441     /* process entries in directory first */
1442     pc->skip_next_fi_callback = GNUNET_YES;
1443     GNUNET_FS_file_information_inspect (fi, &fip_signal_stop, pc);
1444   }
1445   if (NULL != fi->serialization)
1446   {
1447     GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_FILE_INFO,
1448                                  fi->serialization);
1449     GNUNET_free (fi->serialization);
1450     fi->serialization = NULL;
1451   }
1452   off = (fi->chk_uri == NULL) ? 0 : length;
1453   pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
1454   GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, fi, off));
1455   *client_info = NULL;
1456   return GNUNET_OK;
1457 }
1458
1459
1460 /**
1461  * Stop an upload.  Will abort incomplete uploads (but
1462  * not remove blocks that have already been publishd) or
1463  * simply clean up the state for completed uploads.
1464  * Must NOT be called from within the event callback!
1465  *
1466  * @param pc context for the upload to stop
1467  */
1468 void
1469 GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *pc)
1470 {
1471   struct GNUNET_FS_ProgressInfo pi;
1472   uint64_t off;
1473
1474   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1475               "Publish stop called\n");
1476   GNUNET_FS_end_top (pc->h, pc->top);
1477   if (NULL != pc->ksk_pc)
1478   {
1479     GNUNET_FS_publish_ksk_cancel (pc->ksk_pc);
1480     pc->ksk_pc = NULL;
1481   }
1482   if (NULL != pc->sks_pc)
1483   {
1484     GNUNET_FS_publish_sks_cancel (pc->sks_pc);
1485     pc->sks_pc = NULL;
1486   }
1487   if (NULL != pc->upload_task)
1488   {
1489     GNUNET_SCHEDULER_cancel (pc->upload_task);
1490     pc->upload_task = NULL;
1491   }
1492   pc->skip_next_fi_callback = GNUNET_YES;
1493   GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_stop, pc);
1494
1495   if (NULL != pc->fi->serialization)
1496   {
1497     GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_FILE_INFO,
1498                                  pc->fi->serialization);
1499     GNUNET_free (pc->fi->serialization);
1500     pc->fi->serialization = NULL;
1501   }
1502   off = (NULL == pc->fi->chk_uri) ? 0 : GNUNET_ntohll (pc->fi->chk_uri->data.chk.file_length);
1503
1504   if (NULL != pc->serialization)
1505   {
1506     GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_MASTER_PUBLISH,
1507                                  pc->serialization);
1508     GNUNET_free (pc->serialization);
1509     pc->serialization = NULL;
1510   }
1511   if (NULL != pc->qre)
1512   {
1513     GNUNET_DATASTORE_cancel (pc->qre);
1514     pc->qre = NULL;
1515   }
1516   pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
1517   GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, pc->fi, off));
1518   publish_cleanup (pc);
1519 }
1520
1521
1522 /* end of fs_publish.c */