tolerate additional IPv4 address now available for gnunet.org
[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 it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
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->mq)
96   {
97     GNUNET_MQ_destroy (pc->mq);
98     pc->mq = 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                                           &finish_release_reserve, pc);
270   }
271   else
272   {
273     finish_release_reserve (pc, GNUNET_OK, GNUNET_TIME_UNIT_ZERO_ABS, NULL);
274   }
275 }
276
277
278 /**
279  * We are almost done publishing the structure,
280  * add SBlocks (if needed).
281  *
282  * @param pc overall upload data
283  */
284 static void
285 publish_sblock (struct GNUNET_FS_PublishContext *pc)
286 {
287   if (NULL != pc->ns)
288     pc->sks_pc = GNUNET_FS_publish_sks (pc->h,
289                                         pc->ns,
290                                         pc->nid,
291                                         pc->nuid,
292                                         pc->fi->meta,
293                                         pc->fi->chk_uri,
294                                         &pc->fi->bo,
295                                         pc->options,
296                                         &publish_sblocks_cont, pc);
297   else
298     publish_sblocks_cont (pc, NULL, NULL);
299 }
300
301
302 /**
303  * We've finished publishing a KBlock as part of a larger upload.
304  * Check the result and continue the larger upload.
305  *
306  * @param cls the `struct GNUNET_FS_PublishContext *`
307  *        of the larger upload
308  * @param uri URI of the published blocks
309  * @param emsg NULL on success, otherwise error message
310  */
311 static void
312 publish_kblocks_cont (void *cls,
313                       const struct GNUNET_FS_Uri *uri,
314                       const char *emsg)
315 {
316   struct GNUNET_FS_PublishContext *pc = cls;
317   struct GNUNET_FS_FileInformation *p = pc->fi_pos;
318
319   pc->ksk_pc = NULL;
320   if (NULL != emsg)
321   {
322     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
323                 "Error uploading KSK blocks: %s\n",
324                 emsg);
325     signal_publish_error (p, pc, emsg);
326     GNUNET_FS_file_information_sync_ (p);
327     GNUNET_FS_publish_sync_ (pc);
328     GNUNET_assert (NULL == pc->upload_task);
329     pc->upload_task =
330       GNUNET_SCHEDULER_add_with_priority
331       (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
332        &GNUNET_FS_publish_main_,
333        pc);
334     return;
335   }
336   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
337               "KSK blocks published, moving on to next file\n");
338   if (NULL != p->dir)
339     signal_publish_completion (p, pc);
340   /* move on to next file */
341   if (NULL != p->next)
342     pc->fi_pos = p->next;
343   else
344     pc->fi_pos = p->dir;
345   GNUNET_FS_publish_sync_ (pc);
346   GNUNET_assert (NULL == pc->upload_task);
347   pc->upload_task =
348       GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
349                                           &GNUNET_FS_publish_main_, pc);
350 }
351
352
353 /**
354  * Function called by the tree encoder to obtain
355  * a block of plaintext data (for the lowest level
356  * of the tree).
357  *
358  * @param cls our publishing context
359  * @param offset identifies which block to get
360  * @param max (maximum) number of bytes to get; returning
361  *        fewer will also cause errors
362  * @param buf where to copy the plaintext buffer
363  * @param emsg location to store an error message (on error)
364  * @return number of bytes copied to buf, 0 on error
365  */
366 static size_t
367 block_reader (void *cls,
368               uint64_t offset,
369               size_t max,
370               void *buf,
371               char **emsg)
372 {
373   struct GNUNET_FS_PublishContext *pc = cls;
374   struct GNUNET_FS_FileInformation *p;
375   const char *dd;
376   size_t pt_size;
377
378   p = pc->fi_pos;
379   if (GNUNET_YES == p->is_directory)
380   {
381     pt_size = GNUNET_MIN (max, p->data.dir.dir_size - offset);
382     dd = p->data.dir.dir_data;
383     GNUNET_memcpy (buf, &dd[offset], pt_size);
384   }
385   else
386   {
387     if (UINT64_MAX == offset)
388     {
389       if (&GNUNET_FS_data_reader_file_ == p->data.file.reader)
390       {
391         /* force closing the file to avoid keeping too many files open */
392         p->data.file.reader (p->data.file.reader_cls, offset, 0, NULL, NULL);
393       }
394       return 0;
395     }
396     pt_size = GNUNET_MIN (max, p->data.file.file_size - offset);
397     if (0 == pt_size)
398       return 0;                 /* calling reader with pt_size==0
399                                  * might free buf, so don't! */
400     if (pt_size !=
401         p->data.file.reader (p->data.file.reader_cls, offset, pt_size, buf,
402                              emsg))
403       return 0;
404   }
405   return pt_size;
406 }
407
408
409 /**
410  * The tree encoder has finished processing a
411  * file.   Call it's finish method and deal with
412  * the final result.
413  *
414  * @param cls our publishing context
415  */
416 static void
417 encode_cont (void *cls)
418 {
419   struct GNUNET_FS_PublishContext *pc = cls;
420   struct GNUNET_FS_FileInformation *p;
421   struct GNUNET_FS_ProgressInfo pi;
422   char *emsg;
423   uint64_t flen;
424
425   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
426               "Finished with tree encoder\n");
427   p = pc->fi_pos;
428   p->chk_uri = GNUNET_FS_tree_encoder_get_uri (p->te);
429   GNUNET_FS_file_information_sync_ (p);
430   GNUNET_FS_tree_encoder_finish (p->te, &emsg);
431   p->te = NULL;
432   if (NULL != emsg)
433   {
434     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
435                 "Error during tree walk: %s\n",
436                 emsg);
437     GNUNET_asprintf (&p->emsg,
438                      _("Publishing failed: %s"),
439                      emsg);
440     GNUNET_free (emsg);
441     pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
442     pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
443     pi.value.publish.specifics.error.message = p->emsg;
444     p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
445   }
446   else
447   {
448     /* final progress event */
449     GNUNET_assert (NULL != p->chk_uri);
450     flen = GNUNET_FS_uri_chk_get_file_size (p->chk_uri);
451     pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
452     pi.value.publish.specifics.progress.data = NULL;
453     pi.value.publish.specifics.progress.offset = flen;
454     pi.value.publish.specifics.progress.data_len = 0;
455     pi.value.publish.specifics.progress.depth = GNUNET_FS_compute_depth (flen);
456     p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, flen);
457   }
458   /* continue with main */  /* continue with main */
459   GNUNET_assert (NULL == pc->upload_task);
460   pc->upload_task =
461       GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
462                                           &GNUNET_FS_publish_main_, pc);
463 }
464
465
466 /**
467  * Function called asking for the current (encoded)
468  * block to be processed.  After processing the
469  * client should either call #GNUNET_FS_tree_encoder_next
470  * or (on error) #GNUNET_FS_tree_encoder_finish.
471  *
472  * @param cls closure
473  * @param chk content hash key for the block
474  * @param offset offset of the block in the file
475  * @param depth depth of the block in the file, 0 for DBLOCK
476  * @param type type of the block (IBLOCK or DBLOCK)
477  * @param block the (encrypted) block
478  * @param block_size size of @a block (in bytes)
479  */
480 static void
481 block_proc (void *cls,
482             const struct ContentHashKey *chk,
483             uint64_t offset,
484             unsigned int depth,
485             enum GNUNET_BLOCK_Type type,
486             const void *block,
487             uint16_t block_size)
488 {
489   struct GNUNET_FS_PublishContext *pc = cls;
490   struct GNUNET_FS_FileInformation *p;
491   struct OnDemandBlock odb;
492
493   p = pc->fi_pos;
494   if (NULL == pc->dsh)
495   {
496     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
497                 "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                               &ds_put_cont, pc);
530     return;
531   }
532   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
533               "Publishing block `%s' for offset %llu with size %u\n",
534               GNUNET_h2s (&chk->query),
535               (unsigned long long) offset,
536               (unsigned int) block_size);
537   GNUNET_assert (pc->qre == NULL);
538   pc->qre =
539       GNUNET_DATASTORE_put (pc->dsh, (p->is_directory == GNUNET_YES) ? 0 : pc->rid,
540                             &chk->query,
541                             block_size,
542                             block,
543                             type,
544                             p->bo.content_priority,
545                             p->bo.anonymity_level,
546                             p->bo.replication_level,
547                             p->bo.expiration_time,
548                             -2, 1,
549                             &ds_put_cont,
550                             pc);
551 }
552
553
554 /**
555  * Function called with information about our
556  * progress in computing the tree encoding.
557  *
558  * @param cls closure
559  * @param offset where are we in the file
560  * @param pt_block plaintext of the currently processed block
561  * @param pt_size size of @a pt_block
562  * @param depth depth of the block in the tree, 0 for DBLOCK
563  */
564 static void
565 progress_proc (void *cls, uint64_t offset,
566                const void *pt_block,
567                size_t pt_size,
568                unsigned int depth)
569 {
570   struct GNUNET_FS_PublishContext *pc = cls;
571   struct GNUNET_FS_FileInformation *p;
572   struct GNUNET_FS_FileInformation *par;
573   struct GNUNET_FS_ProgressInfo pi;
574
575   p = pc->fi_pos;
576   pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
577   pi.value.publish.specifics.progress.data = pt_block;
578   pi.value.publish.specifics.progress.offset = offset;
579   pi.value.publish.specifics.progress.data_len = pt_size;
580   pi.value.publish.specifics.progress.depth = depth;
581   p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, offset);
582   if ( (0 != depth) ||
583        (GNUNET_YES == p->is_directory) )
584     return;
585   while (NULL != (par = p->dir))
586   {
587     p = par;
588     GNUNET_assert (GNUNET_YES == par->is_directory);
589     p->data.dir.contents_completed += pt_size;
590     pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY;
591     pi.value.publish.specifics.progress_directory.completed = p->data.dir.contents_completed;
592     pi.value.publish.specifics.progress_directory.total = p->data.dir.contents_size;
593     pi.value.publish.specifics.progress_directory.eta = GNUNET_TIME_calculate_eta (p->start_time,
594                                                                                    p->data.dir.contents_completed,
595                                                                                    p->data.dir.contents_size);
596     p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
597
598   }
599 }
600
601
602 /**
603  * We are uploading a file or directory; load (if necessary) the next
604  * block into memory, encrypt it and send it to the FS service.  Then
605  * continue with the main task.
606  *
607  * @param pc overall upload data
608  */
609 static void
610 publish_content (struct GNUNET_FS_PublishContext *pc)
611 {
612   struct GNUNET_FS_FileInformation *p;
613   char *emsg;
614   struct GNUNET_FS_DirectoryBuilder *db;
615   struct GNUNET_FS_FileInformation *dirpos;
616   void *raw_data;
617   uint64_t size;
618
619   p = pc->fi_pos;
620   GNUNET_assert (NULL != p);
621   if (NULL == p->te)
622   {
623     if (GNUNET_YES == p->is_directory)
624     {
625       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating directory\n");
626       db = GNUNET_FS_directory_builder_create (p->meta);
627       dirpos = p->data.dir.entries;
628       while (NULL != dirpos)
629       {
630         if (GNUNET_YES == dirpos->is_directory)
631         {
632           raw_data = dirpos->data.dir.dir_data;
633           dirpos->data.dir.dir_data = NULL;
634         }
635         else
636         {
637           raw_data = NULL;
638           if ((dirpos->data.file.file_size < MAX_INLINE_SIZE) &&
639               (dirpos->data.file.file_size > 0))
640           {
641             raw_data = GNUNET_malloc (dirpos->data.file.file_size);
642             emsg = NULL;
643             if (dirpos->data.file.file_size !=
644                 dirpos->data.file.reader (dirpos->data.file.reader_cls, 0,
645                                           dirpos->data.file.file_size, raw_data,
646                                           &emsg))
647             {
648               GNUNET_free_non_null (emsg);
649               GNUNET_free (raw_data);
650               raw_data = NULL;
651             }
652             dirpos->data.file.reader (dirpos->data.file.reader_cls, UINT64_MAX, 0, 0, NULL);
653           }
654         }
655         GNUNET_FS_directory_builder_add (db, dirpos->chk_uri, dirpos->meta,
656                                          raw_data);
657         GNUNET_free_non_null (raw_data);
658         dirpos = dirpos->next;
659       }
660       GNUNET_free_non_null (p->data.dir.dir_data);
661       p->data.dir.dir_data = NULL;
662       p->data.dir.dir_size = 0;
663       GNUNET_FS_directory_builder_finish (db, &p->data.dir.dir_size,
664                                           &p->data.dir.dir_data);
665       GNUNET_FS_file_information_sync_ (p);
666     }
667     size = (GNUNET_YES == p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
668     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
669                 "Creating tree encoder\n");
670     p->te =
671         GNUNET_FS_tree_encoder_create (pc->h, size, pc, &block_reader,
672                                        &block_proc, &progress_proc,
673                                        &encode_cont);
674
675   }
676   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
677               "Processing next block from tree\n");
678   GNUNET_FS_tree_encoder_next (p->te);
679 }
680
681
682 /**
683  * Check the response from the "fs" service to our 'start index'
684  * request.
685  *
686  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
687  * @param msg the response we got
688  */
689 static int
690 check_index_start_failed (void *cls,
691                           const struct GNUNET_MessageHeader *msg)
692 {
693   size_t msize = ntohs (msg->size) - sizeof (*msg);
694   const char *emsg = (const char *) &msg[1];
695
696   if (emsg[msize - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
697   {
698     GNUNET_break (0);
699     return GNUNET_SYSERR;
700   }
701   return GNUNET_OK;
702 }
703
704
705 /**
706  * Process the response from the "fs" service to our 'start index'
707  * request.
708  *
709  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
710  * @param msg the response we got
711  */
712 static void
713 handle_index_start_failed (void *cls,
714                            const struct GNUNET_MessageHeader *msg)
715 {
716   struct GNUNET_FS_PublishContext *pc = cls;
717   struct GNUNET_FS_FileInformation *p;
718   const char *emsg = (const char *) &msg[1];
719   char *msgtxt;
720
721   GNUNET_MQ_destroy (pc->mq);
722   pc->mq = NULL;
723   p = pc->fi_pos;
724   GNUNET_asprintf (&msgtxt,
725                    _("Can not index file `%s': %s.\n"),
726                    p->filename,
727                    gettext (emsg));
728   signal_publish_error (p,
729                         pc,
730                         msgtxt);
731   GNUNET_free (msgtxt);
732   GNUNET_FS_file_information_sync_ (p);
733   GNUNET_FS_publish_sync_ (pc);
734 }
735
736
737 /**
738  * Process the response from the "fs" service to our 'start index'
739  * request.
740  *
741  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
742  * @param msg the response we got
743  */
744 static void
745 handle_index_start_ok (void *cls,
746                        const struct GNUNET_MessageHeader *msg)
747 {
748   struct GNUNET_FS_PublishContext *pc = cls;
749   struct GNUNET_FS_FileInformation *p;
750
751   GNUNET_MQ_destroy (pc->mq);
752   pc->mq = NULL;
753   p = pc->fi_pos;
754   p->data.file.index_start_confirmed = GNUNET_YES;
755   GNUNET_FS_file_information_sync_ (p);
756   publish_content (pc);
757 }
758
759
760 /**
761  * Generic error handler, called with the appropriate error code and
762  * the same closure specified at the creation of the message queue.
763  * Not every message queue implementation supports an error handler.
764  *
765  * @param cls closure with the `struct GNUNET_FS_PublishContext *`
766  * @param error error code
767  */
768 static void
769 index_mq_error_handler (void *cls,
770                         enum GNUNET_MQ_Error error)
771 {
772   struct GNUNET_FS_PublishContext *pc = cls;
773   struct GNUNET_FS_FileInformation *p;
774
775   if (NULL != pc->mq)
776   {
777     GNUNET_MQ_destroy (pc->mq);
778     pc->mq = NULL;
779   }
780   p = pc->fi_pos;
781   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
782               _("Can not index file `%s': %s.  Will try to insert instead.\n"),
783               p->filename,
784               _("error on index-start request to `fs' service"));
785   p->data.file.do_index = GNUNET_NO;
786   GNUNET_FS_file_information_sync_ (p);
787   publish_content (pc);
788 }
789
790
791 /**
792  * Function called once the hash computation over an
793  * indexed file has completed.
794  *
795  * @param cls closure, our publishing context
796  * @param res resulting hash, NULL on error
797  */
798 static void
799 hash_for_index_cb (void *cls,
800                    const struct GNUNET_HashCode *res)
801 {
802   struct GNUNET_FS_PublishContext *pc = cls;
803   struct GNUNET_MQ_MessageHandler handlers[] = {
804     GNUNET_MQ_hd_fixed_size (index_start_ok,
805                              GNUNET_MESSAGE_TYPE_FS_INDEX_START_OK,
806                              struct GNUNET_MessageHeader,
807                              pc),
808     GNUNET_MQ_hd_var_size (index_start_failed,
809                            GNUNET_MESSAGE_TYPE_FS_INDEX_START_FAILED,
810                            struct GNUNET_MessageHeader,
811                            pc),
812     GNUNET_MQ_handler_end ()
813   };
814   struct GNUNET_FS_FileInformation *p;
815   struct GNUNET_MQ_Envelope *env;
816   struct IndexStartMessage *ism;
817   size_t slen;
818   uint64_t dev;
819   uint64_t ino;
820   char *fn;
821
822   pc->fhc = NULL;
823   p = pc->fi_pos;
824   if (NULL == res)
825   {
826     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
827                 _("Can not index file `%s': %s.  Will try to insert instead.\n"),
828                 p->filename,
829                 _("failed to compute hash"));
830     p->data.file.do_index = GNUNET_NO;
831     GNUNET_FS_file_information_sync_ (p);
832     publish_content (pc);
833     return;
834   }
835   if (GNUNET_YES == p->data.file.index_start_confirmed)
836   {
837     publish_content (pc);
838     return;
839   }
840   fn = GNUNET_STRINGS_filename_expand (p->filename);
841   GNUNET_assert (fn != NULL);
842   slen = strlen (fn) + 1;
843   if (slen >=
844       GNUNET_MAX_MESSAGE_SIZE - sizeof (struct IndexStartMessage))
845   {
846     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
847                 _
848                 ("Can not index file `%s': %s.  Will try to insert instead.\n"),
849                 fn, _("filename too long"));
850     GNUNET_free (fn);
851     p->data.file.do_index = GNUNET_NO;
852     GNUNET_FS_file_information_sync_ (p);
853     publish_content (pc);
854     return;
855   }
856   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
857               "Hash of indexed file `%s' is `%s'\n",
858               p->filename,
859               GNUNET_h2s (res));
860   if (0 != (pc->options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
861   {
862     p->data.file.file_id = *res;
863     p->data.file.have_hash = GNUNET_YES;
864     p->data.file.index_start_confirmed = GNUNET_YES;
865     GNUNET_FS_file_information_sync_ (p);
866     publish_content (pc);
867     GNUNET_free (fn);
868     return;
869   }
870   pc->mq = GNUNET_CLIENT_connect (pc->h->cfg,
871                                   "fs",
872                                   handlers,
873                                   &index_mq_error_handler,
874                                   pc);
875   if (NULL == pc->mq)
876   {
877     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
878                 _("Can not index file `%s': %s.  Will try to insert instead.\n"),
879                 p->filename,
880                 _("could not connect to `fs' service"));
881     p->data.file.do_index = GNUNET_NO;
882     publish_content (pc);
883     GNUNET_free (fn);
884     return;
885   }
886   if (p->data.file.have_hash != GNUNET_YES)
887   {
888     p->data.file.file_id = *res;
889     p->data.file.have_hash = GNUNET_YES;
890     GNUNET_FS_file_information_sync_ (p);
891   }
892   env = GNUNET_MQ_msg_extra (ism,
893                              slen,
894                              GNUNET_MESSAGE_TYPE_FS_INDEX_START);
895   if (GNUNET_OK ==
896       GNUNET_DISK_file_get_identifiers (p->filename,
897                                         &dev,
898                                         &ino))
899   {
900     ism->device = GNUNET_htonll (dev);
901     ism->inode = GNUNET_htonll (ino);
902   }
903   else
904   {
905     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
906                 _("Failed to get file identifiers for `%s'\n"),
907                 p->filename);
908   }
909   ism->file_id = *res;
910   GNUNET_memcpy (&ism[1],
911           fn,
912           slen);
913   GNUNET_free (fn);
914   GNUNET_MQ_send (pc->mq,
915                   env);
916 }
917
918
919 /**
920  * We've computed the CHK/LOC URI, now publish the KSKs (if applicable).
921  *
922  * @param pc publishing context to do this for
923  */
924 static void
925 publish_kblocks (struct GNUNET_FS_PublishContext *pc)
926 {
927   struct GNUNET_FS_FileInformation *p;
928
929   p = pc->fi_pos;
930   /* upload of "p" complete, publish KBlocks! */
931   if (NULL != p->keywords)
932   {
933     pc->ksk_pc = GNUNET_FS_publish_ksk (pc->h,
934                                         p->keywords,
935                                         p->meta,
936                                         p->chk_uri,
937                                         &p->bo,
938                                         pc->options,
939                                         &publish_kblocks_cont,
940                                         pc);
941   }
942   else
943   {
944     publish_kblocks_cont (pc, p->chk_uri, NULL);
945   }
946 }
947
948
949 /**
950  * Process the response from the "fs" service to our LOC sign request.
951  *
952  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
953  * @param sig the response we got
954  */
955 static void
956 handle_signature_response (void *cls,
957                            const struct ResponseLocSignatureMessage *sig)
958 {
959   struct GNUNET_FS_PublishContext *pc = cls;
960   struct GNUNET_FS_FileInformation *p;
961
962   p = pc->fi_pos;
963   p->chk_uri->type = GNUNET_FS_URI_LOC;
964   /* p->data.loc.fi kept from CHK before */
965   p->chk_uri->data.loc.peer = sig->peer;
966   p->chk_uri->data.loc.expirationTime
967     = GNUNET_TIME_absolute_ntoh (sig->expiration_time);
968   p->chk_uri->data.loc.contentSignature = sig->signature;
969   GNUNET_FS_file_information_sync_ (p);
970   GNUNET_FS_publish_sync_ (pc);
971   publish_kblocks (pc);
972 }
973
974
975 /**
976  * Generic error handler, called with the appropriate error code and
977  * the same closure specified at the creation of the message queue.
978  * Not every message queue implementation supports an error handler.
979  *
980  * @param cls closure with the `struct GNUNET_FS_PublishContext *`
981  * @param error error code
982  */
983 static void
984 loc_mq_error_handler (void *cls,
985                       enum GNUNET_MQ_Error error)
986 {
987   struct GNUNET_FS_PublishContext *pc = cls;
988
989   if (NULL != pc->mq)
990   {
991     GNUNET_MQ_destroy (pc->mq);
992     pc->mq = NULL;
993   }
994   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
995               _("Can not create LOC URI. Will continue with CHK instead.\n"));
996   publish_kblocks (pc);
997 }
998
999
1000 /**
1001  * We're publishing without anonymity. Contact the FS service
1002  * to create a signed LOC URI for further processing, then
1003  * continue with KSKs.
1004  *
1005  * @param pc the publishing context do to this for
1006  */
1007 static void
1008 create_loc_uri (struct GNUNET_FS_PublishContext *pc)
1009 {
1010   struct GNUNET_MQ_MessageHandler handlers[] = {
1011     GNUNET_MQ_hd_fixed_size (signature_response,
1012                              GNUNET_MESSAGE_TYPE_FS_REQUEST_LOC_SIGNATURE,
1013                              struct ResponseLocSignatureMessage,
1014                              pc),
1015     GNUNET_MQ_handler_end ()
1016   };
1017   struct GNUNET_MQ_Envelope *env;
1018   struct RequestLocSignatureMessage *req;
1019   struct GNUNET_FS_FileInformation *p;
1020
1021   if (NULL != pc->mq)
1022     GNUNET_MQ_destroy (pc->mq);
1023   pc->mq = GNUNET_CLIENT_connect (pc->h->cfg,
1024                                   "fs",
1025                                   handlers,
1026                                   &loc_mq_error_handler,
1027                                   pc);
1028   if (NULL == pc->mq)
1029   {
1030     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1031                 _("Can not create LOC URI. Will continue with CHK instead.\n"));
1032     publish_kblocks (pc);
1033     return;
1034   }
1035   p = pc->fi_pos;
1036   env = GNUNET_MQ_msg (req,
1037                        GNUNET_MESSAGE_TYPE_FS_REQUEST_LOC_SIGN);
1038   req->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT);
1039   req->expiration_time = GNUNET_TIME_absolute_hton (p->bo.expiration_time);
1040   req->chk = p->chk_uri->data.chk.chk;
1041   req->file_length = GNUNET_htonll (p->chk_uri->data.chk.file_length);
1042   GNUNET_MQ_send (pc->mq,
1043                   env);
1044 }
1045
1046
1047 /**
1048  * Main function that performs the upload.
1049  *
1050  * @param cls `struct GNUNET_FS_PublishContext *` identifies the upload
1051  */
1052 void
1053 GNUNET_FS_publish_main_ (void *cls)
1054 {
1055   struct GNUNET_FS_PublishContext *pc = cls;
1056   struct GNUNET_FS_ProgressInfo pi;
1057   struct GNUNET_FS_FileInformation *p;
1058   char *fn;
1059
1060   pc->upload_task = NULL;
1061   p = pc->fi_pos;
1062   if (NULL == p)
1063   {
1064     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1065                 "Publishing complete, now publishing SKS and KSK blocks.\n");
1066     /* upload of entire hierarchy complete,
1067      * publish namespace entries */
1068     GNUNET_FS_publish_sync_ (pc);
1069     publish_sblock (pc);
1070     return;
1071   }
1072   /* find starting position */
1073   while ( (GNUNET_YES == p->is_directory) &&
1074           (NULL != p->data.dir.entries) &&
1075           (NULL == p->emsg) &&
1076           (NULL == p->data.dir.entries->chk_uri) )
1077   {
1078     p = p->data.dir.entries;
1079     pc->fi_pos = p;
1080     GNUNET_FS_publish_sync_ (pc);
1081   }
1082   /* abort on error */
1083   if (NULL != p->emsg)
1084   {
1085     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1086                 "Error uploading: %s\n",
1087                 p->emsg);
1088     /* error with current file, abort all
1089      * related files as well! */
1090     while (NULL != p->dir)
1091     {
1092       fn = GNUNET_CONTAINER_meta_data_get_by_type (p->meta,
1093                                                    EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
1094       p = p->dir;
1095       if (fn != NULL)
1096       {
1097         GNUNET_asprintf (&p->emsg,
1098                          _("Recursive upload failed at `%s': %s"),
1099                          fn,
1100                          p->emsg);
1101         GNUNET_free (fn);
1102       }
1103       else
1104       {
1105         GNUNET_asprintf (&p->emsg,
1106                          _("Recursive upload failed: %s"),
1107                          p->emsg);
1108       }
1109       pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
1110       pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
1111       pi.value.publish.specifics.error.message = p->emsg;
1112       p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
1113     }
1114     pc->all_done = GNUNET_YES;
1115     GNUNET_FS_publish_sync_ (pc);
1116     return;
1117   }
1118   /* handle completion */
1119   if (NULL != p->chk_uri)
1120   {
1121     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1122                 "File upload complete, now publishing KSK blocks.\n");
1123     GNUNET_FS_publish_sync_ (pc);
1124
1125     if ( (0 == p->bo.anonymity_level) &&
1126          (GNUNET_YES !=
1127           GNUNET_FS_uri_test_loc (p->chk_uri)) )
1128     {
1129       /* zero anonymity, box CHK URI in LOC URI */
1130       create_loc_uri (pc);
1131     }
1132     else
1133     {
1134       publish_kblocks (pc);
1135     }
1136     return;
1137   }
1138   if ((GNUNET_YES != p->is_directory) && (p->data.file.do_index))
1139   {
1140     if (NULL == p->filename)
1141     {
1142       p->data.file.do_index = GNUNET_NO;
1143       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1144                   _("Can not index file `%s': %s.  Will try to insert instead.\n"),
1145                   "<no-name>",
1146                   _("needs to be an actual file"));
1147       GNUNET_FS_file_information_sync_ (p);
1148       publish_content (pc);
1149       return;
1150     }
1151     if (p->data.file.have_hash)
1152     {
1153       hash_for_index_cb (pc, &p->data.file.file_id);
1154     }
1155     else
1156     {
1157       p->start_time = GNUNET_TIME_absolute_get ();
1158       pc->fhc =
1159           GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE, p->filename,
1160                                    HASHING_BLOCKSIZE, &hash_for_index_cb, pc);
1161     }
1162     return;
1163   }
1164   publish_content (pc);
1165 }
1166
1167
1168 /**
1169  * Signal the FS's progress function that we are starting
1170  * an upload.
1171  *
1172  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
1173  * @param fi the entry in the publish-structure
1174  * @param length length of the file or directory
1175  * @param meta metadata for the file or directory (can be modified)
1176  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1177  * @param bo block options
1178  * @param do_index should we index?
1179  * @param client_info pointer to client context set upon creation (can be modified)
1180  * @return #GNUNET_OK to continue (always)
1181  */
1182 static int
1183 fip_signal_start (void *cls,
1184                   struct GNUNET_FS_FileInformation *fi,
1185                   uint64_t length,
1186                   struct GNUNET_CONTAINER_MetaData *meta,
1187                   struct GNUNET_FS_Uri **uri,
1188                   struct GNUNET_FS_BlockOptions *bo,
1189                   int *do_index,
1190                   void **client_info)
1191 {
1192   struct GNUNET_FS_PublishContext *pc = cls;
1193   struct GNUNET_FS_ProgressInfo pi;
1194   unsigned int kc;
1195   uint64_t left;
1196
1197   if (GNUNET_YES == pc->skip_next_fi_callback)
1198   {
1199     pc->skip_next_fi_callback = GNUNET_NO;
1200     return GNUNET_OK;
1201   }
1202   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1203               "Starting publish operation\n");
1204   if (*do_index)
1205   {
1206     /* space for on-demand blocks */
1207     pc->reserve_space +=
1208         ((length + DBLOCK_SIZE -
1209           1) / DBLOCK_SIZE) * sizeof (struct OnDemandBlock);
1210   }
1211   else
1212   {
1213     /* space for DBlocks */
1214     pc->reserve_space += length;
1215   }
1216   /* entries for IBlocks and DBlocks, space for IBlocks */
1217   left = length;
1218   while (1)
1219   {
1220     left = (left + DBLOCK_SIZE - 1) / DBLOCK_SIZE;
1221     pc->reserve_entries += left;
1222     if (left <= 1)
1223       break;
1224     left = left * sizeof (struct ContentHashKey);
1225     pc->reserve_space += left;
1226   }
1227   pc->reserve_entries++;
1228   /* entries and space for keywords */
1229   if (NULL != *uri)
1230   {
1231     kc = GNUNET_FS_uri_ksk_get_keyword_count (*uri);
1232     pc->reserve_entries += kc;
1233     pc->reserve_space += GNUNET_MAX_MESSAGE_SIZE * kc;
1234   }
1235   pi.status = GNUNET_FS_STATUS_PUBLISH_START;
1236   *client_info = GNUNET_FS_publish_make_status_ (&pi, pc, fi, 0);
1237   GNUNET_FS_file_information_sync_ (fi);
1238   if ((fi->is_directory) && (fi->dir != NULL))
1239   {
1240     /* We are a directory, and we are not top-level; process entries in directory */
1241     pc->skip_next_fi_callback = GNUNET_YES;
1242     GNUNET_FS_file_information_inspect (fi, &fip_signal_start, pc);
1243   }
1244   return GNUNET_OK;
1245 }
1246
1247
1248 /**
1249  * Actually signal the FS's progress function that we are suspending
1250  * an upload.
1251  *
1252  * @param fi the entry in the publish-structure
1253  * @param pc the publish context of which a file is being suspended
1254  */
1255 static void
1256 suspend_operation (struct GNUNET_FS_FileInformation *fi,
1257                    struct GNUNET_FS_PublishContext *pc)
1258 {
1259   struct GNUNET_FS_ProgressInfo pi;
1260   uint64_t off;
1261
1262   if (NULL != pc->ksk_pc)
1263   {
1264     GNUNET_FS_publish_ksk_cancel (pc->ksk_pc);
1265     pc->ksk_pc = NULL;
1266   }
1267   if (NULL != pc->sks_pc)
1268   {
1269     GNUNET_FS_publish_sks_cancel (pc->sks_pc);
1270     pc->sks_pc = NULL;
1271   }
1272   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1273               "Suspending publish operation\n");
1274   GNUNET_free_non_null (fi->serialization);
1275   fi->serialization = NULL;
1276   off = (NULL == fi->chk_uri) ? 0 : (GNUNET_YES == fi->is_directory) ? fi->data.dir.dir_size : fi->data.file.file_size;
1277   pi.status = GNUNET_FS_STATUS_PUBLISH_SUSPEND;
1278   GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, fi, off));
1279   if (NULL != pc->qre)
1280   {
1281     GNUNET_DATASTORE_cancel (pc->qre);
1282     pc->qre = NULL;
1283   }
1284   if (NULL != pc->dsh)
1285   {
1286     GNUNET_DATASTORE_disconnect (pc->dsh, GNUNET_NO);
1287     pc->dsh = NULL;
1288   }
1289   pc->rid = 0;
1290 }
1291
1292
1293 /**
1294  * Signal the FS's progress function that we are suspending
1295  * an upload.  Performs the recursion.
1296  *
1297  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
1298  * @param fi the entry in the publish-structure
1299  * @param length length of the file or directory
1300  * @param meta metadata for the file or directory (can be modified)
1301  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1302  * @param bo block options
1303  * @param do_index should we index?
1304  * @param client_info pointer to client context set upon creation (can be modified)
1305  * @return #GNUNET_OK to continue (always)
1306  */
1307 static int
1308 fip_signal_suspend (void *cls,
1309                     struct GNUNET_FS_FileInformation *fi,
1310                     uint64_t length,
1311                     struct GNUNET_CONTAINER_MetaData *meta,
1312                     struct GNUNET_FS_Uri **uri,
1313                     struct GNUNET_FS_BlockOptions *bo,
1314                     int *do_index,
1315                     void **client_info)
1316 {
1317   struct GNUNET_FS_PublishContext *pc = cls;
1318
1319   if (GNUNET_YES == pc->skip_next_fi_callback)
1320   {
1321     pc->skip_next_fi_callback = GNUNET_NO;
1322     return GNUNET_OK;
1323   }
1324   if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta))
1325   {
1326     /* process entries in directory */
1327     pc->skip_next_fi_callback = GNUNET_YES;
1328     GNUNET_FS_file_information_inspect (fi, &fip_signal_suspend, pc);
1329   }
1330   suspend_operation (fi, pc);
1331   *client_info = NULL;
1332   return GNUNET_OK;
1333 }
1334
1335
1336 /**
1337  * Create SUSPEND event for the given publish operation
1338  * and then clean up our state (without stop signal).
1339  *
1340  * @param cls the `struct GNUNET_FS_PublishContext` to signal for
1341  */
1342 void
1343 GNUNET_FS_publish_signal_suspend_ (void *cls)
1344 {
1345   struct GNUNET_FS_PublishContext *pc = cls;
1346
1347   if (NULL != pc->upload_task)
1348   {
1349     GNUNET_SCHEDULER_cancel (pc->upload_task);
1350     pc->upload_task = NULL;
1351   }
1352   pc->skip_next_fi_callback = GNUNET_YES;
1353   GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_suspend, pc);
1354   suspend_operation (pc->fi, pc);
1355   GNUNET_FS_end_top (pc->h, pc->top);
1356   pc->top = NULL;
1357   publish_cleanup (pc);
1358 }
1359
1360
1361 /**
1362  * We have gotten a reply for our space reservation request.
1363  * Either fail (insufficient space) or start publishing for good.
1364  *
1365  * @param cls the `struct GNUNET_FS_PublishContext *`
1366  * @param success positive reservation ID on success
1367  * @param min_expiration minimum expiration time required for content to be stored
1368  * @param msg error message on error, otherwise NULL
1369  */
1370 static void
1371 finish_reserve (void *cls,
1372                 int success,
1373                 struct GNUNET_TIME_Absolute min_expiration,
1374                 const char *msg)
1375 {
1376   struct GNUNET_FS_PublishContext *pc = cls;
1377
1378   pc->qre = NULL;
1379   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1380               "Reservation complete (%d)!\n",
1381               success);
1382   if ((msg != NULL) || (success <= 0))
1383   {
1384     GNUNET_asprintf (&pc->fi->emsg,
1385                      _("Datastore failure: %s"),
1386                      msg);
1387     signal_publish_error (pc->fi, pc, pc->fi->emsg);
1388     return;
1389   }
1390   pc->rid = success;
1391   GNUNET_assert (NULL == pc->upload_task);
1392   pc->upload_task =
1393       GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
1394                                           &GNUNET_FS_publish_main_, pc);
1395 }
1396
1397
1398 /**
1399  * Calculate the total size of all of the files in the directory structure.
1400  *
1401  * @param fi file structure to traverse
1402  */
1403 static uint64_t
1404 compute_contents_size (struct GNUNET_FS_FileInformation *fi)
1405 {
1406   struct GNUNET_FS_FileInformation *ent;
1407
1408   if (GNUNET_YES != fi->is_directory)
1409     return fi->data.file.file_size;
1410   fi->data.dir.contents_size = 0;
1411   for (ent = fi->data.dir.entries; NULL != ent; ent = ent->next)
1412     fi->data.dir.contents_size += compute_contents_size (ent);
1413   return fi->data.dir.contents_size;
1414 }
1415
1416
1417 /**
1418  * Publish a file or directory.
1419  *
1420  * @param h handle to the file sharing subsystem
1421  * @param fi information about the file or directory structure to publish
1422  * @param ns namespace to publish the file in, NULL for no namespace
1423  * @param nid identifier to use for the publishd content in the namespace
1424  *        (can be NULL, must be NULL if namespace is NULL)
1425  * @param nuid update-identifier that will be used for future updates
1426  *        (can be NULL, must be NULL if namespace or nid is NULL)
1427  * @param options options for the publication
1428  * @return context that can be used to control the publish operation
1429  */
1430 struct GNUNET_FS_PublishContext *
1431 GNUNET_FS_publish_start (struct GNUNET_FS_Handle *h,
1432                          struct GNUNET_FS_FileInformation *fi,
1433                          const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns,
1434                          const char *nid,
1435                          const char *nuid,
1436                          enum GNUNET_FS_PublishOptions options)
1437 {
1438   struct GNUNET_FS_PublishContext *ret;
1439   struct GNUNET_DATASTORE_Handle *dsh;
1440
1441   GNUNET_assert (NULL != h);
1442   compute_contents_size (fi);
1443   if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1444   {
1445     dsh = GNUNET_DATASTORE_connect (h->cfg);
1446     if (NULL == dsh)
1447       return NULL;
1448   }
1449   else
1450   {
1451     dsh = NULL;
1452   }
1453   ret = GNUNET_new (struct GNUNET_FS_PublishContext);
1454   ret->dsh = dsh;
1455   ret->h = h;
1456   ret->fi = fi;
1457   if (NULL != ns)
1458   {
1459     ret->ns = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey);
1460     *ret->ns = *ns;
1461     GNUNET_assert (NULL != nid);
1462     ret->nid = GNUNET_strdup (nid);
1463     if (NULL != nuid)
1464       ret->nuid = GNUNET_strdup (nuid);
1465   }
1466   ret->options = options;
1467   /* signal start */
1468   GNUNET_FS_file_information_inspect (ret->fi, &fip_signal_start, ret);
1469   ret->fi_pos = ret->fi;
1470   ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_publish_signal_suspend_, ret);
1471   GNUNET_FS_publish_sync_ (ret);
1472   if (NULL != ret->dsh)
1473   {
1474     GNUNET_assert (NULL == ret->qre);
1475     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1476                 _("Reserving space for %u entries and %llu bytes for publication\n"),
1477                 (unsigned int) ret->reserve_entries,
1478                 (unsigned long long) ret->reserve_space);
1479     ret->qre =
1480         GNUNET_DATASTORE_reserve (ret->dsh, ret->reserve_space,
1481                                   ret->reserve_entries,
1482                                   &finish_reserve,
1483                                   ret);
1484   }
1485   else
1486   {
1487     GNUNET_assert (NULL == ret->upload_task);
1488     ret->upload_task =
1489         GNUNET_SCHEDULER_add_with_priority
1490         (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, &GNUNET_FS_publish_main_, ret);
1491   }
1492   return ret;
1493 }
1494
1495
1496 /**
1497  * Signal the FS's progress function that we are stopping
1498  * an upload.
1499  *
1500  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
1501  * @param fi the entry in the publish-structure
1502  * @param length length of the file or directory
1503  * @param meta metadata for the file or directory (can be modified)
1504  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1505  * @param bo block options (can be modified)
1506  * @param do_index should we index?
1507  * @param client_info pointer to client context set upon creation (can be modified)
1508  * @return #GNUNET_OK to continue (always)
1509  */
1510 static int
1511 fip_signal_stop (void *cls,
1512                  struct GNUNET_FS_FileInformation *fi,
1513                  uint64_t length,
1514                  struct GNUNET_CONTAINER_MetaData *meta,
1515                  struct GNUNET_FS_Uri **uri,
1516                  struct GNUNET_FS_BlockOptions *bo,
1517                  int *do_index, void **client_info)
1518 {
1519   struct GNUNET_FS_PublishContext *pc = cls;
1520   struct GNUNET_FS_ProgressInfo pi;
1521   uint64_t off;
1522
1523   if (GNUNET_YES == pc->skip_next_fi_callback)
1524   {
1525     pc->skip_next_fi_callback = GNUNET_NO;
1526     return GNUNET_OK;
1527   }
1528   if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta))
1529   {
1530     /* process entries in directory first */
1531     pc->skip_next_fi_callback = GNUNET_YES;
1532     GNUNET_FS_file_information_inspect (fi, &fip_signal_stop, pc);
1533   }
1534   if (NULL != fi->serialization)
1535   {
1536     GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_FILE_INFO,
1537                                  fi->serialization);
1538     GNUNET_free (fi->serialization);
1539     fi->serialization = NULL;
1540   }
1541   off = (fi->chk_uri == NULL) ? 0 : length;
1542   pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
1543   GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, fi, off));
1544   *client_info = NULL;
1545   return GNUNET_OK;
1546 }
1547
1548
1549 /**
1550  * Stop an upload.  Will abort incomplete uploads (but
1551  * not remove blocks that have already been publishd) or
1552  * simply clean up the state for completed uploads.
1553  * Must NOT be called from within the event callback!
1554  *
1555  * @param pc context for the upload to stop
1556  */
1557 void
1558 GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *pc)
1559 {
1560   struct GNUNET_FS_ProgressInfo pi;
1561   uint64_t off;
1562
1563   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1564               "Publish stop called\n");
1565   GNUNET_FS_end_top (pc->h, pc->top);
1566   if (NULL != pc->ksk_pc)
1567   {
1568     GNUNET_FS_publish_ksk_cancel (pc->ksk_pc);
1569     pc->ksk_pc = NULL;
1570   }
1571   if (NULL != pc->sks_pc)
1572   {
1573     GNUNET_FS_publish_sks_cancel (pc->sks_pc);
1574     pc->sks_pc = NULL;
1575   }
1576   if (NULL != pc->upload_task)
1577   {
1578     GNUNET_SCHEDULER_cancel (pc->upload_task);
1579     pc->upload_task = NULL;
1580   }
1581   pc->skip_next_fi_callback = GNUNET_YES;
1582   GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_stop, pc);
1583
1584   if (NULL != pc->fi->serialization)
1585   {
1586     GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_FILE_INFO,
1587                                  pc->fi->serialization);
1588     GNUNET_free (pc->fi->serialization);
1589     pc->fi->serialization = NULL;
1590   }
1591   off = (NULL == pc->fi->chk_uri) ? 0 : GNUNET_ntohll (pc->fi->chk_uri->data.chk.file_length);
1592
1593   if (NULL != pc->serialization)
1594   {
1595     GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_MASTER_PUBLISH,
1596                                  pc->serialization);
1597     GNUNET_free (pc->serialization);
1598     pc->serialization = NULL;
1599   }
1600   if (NULL != pc->qre)
1601   {
1602     GNUNET_DATASTORE_cancel (pc->qre);
1603     pc->qre = NULL;
1604   }
1605   pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
1606   GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, pc->fi, off));
1607   publish_cleanup (pc);
1608 }
1609
1610
1611 /* end of fs_publish.c */