error handling
[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 :
59     p->data.file.file_size;
60   pi->value.publish.eta =
61     GNUNET_TIME_calculate_eta (p->start_time, offset,
62                                pi->value.publish.size);
63   pi->value.publish.completed = offset;
64   pi->value.publish.duration =
65     GNUNET_TIME_absolute_get_duration (p->start_time);
66   pi->value.publish.anonymity = p->bo.anonymity_level;
67   pi->fsh = pc->h;
68   return pc->h->upcb (pc->h->upcb_cls, pi);
69 }
70
71
72 /**
73  * Cleanup the publish context, we're done with it.
74  *
75  * @param pc struct to clean up
76  */
77 static void
78 publish_cleanup (struct GNUNET_FS_PublishContext *pc)
79 {
80   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
81               "Cleaning up publish context (done!)\n");
82   if (NULL != pc->fhc)
83   {
84     GNUNET_CRYPTO_hash_file_cancel (pc->fhc);
85     pc->fhc = NULL;
86   }
87   GNUNET_FS_file_information_destroy (pc->fi, NULL, NULL);
88   GNUNET_free_non_null (pc->nid);
89   GNUNET_free_non_null (pc->nuid);
90   GNUNET_free_non_null (pc->serialization);
91   if (NULL != pc->dsh)
92   {
93     GNUNET_DATASTORE_disconnect (pc->dsh, GNUNET_NO);
94     pc->dsh = NULL;
95   }
96   if (NULL != pc->mq)
97   {
98     GNUNET_MQ_destroy (pc->mq);
99     pc->mq = NULL;
100   }
101   GNUNET_assert (NULL == pc->upload_task);
102   GNUNET_free (pc);
103 }
104
105
106 /**
107  * Function called by the datastore API with
108  * the result from the PUT request.
109  *
110  * @param cls the `struct GNUNET_FS_PublishContext *`
111  * @param success #GNUNET_OK on success
112  * @param min_expiration minimum expiration time required for content to be stored
113  * @param msg error message (or NULL)
114  */
115 static void
116 ds_put_cont (void *cls,
117              int success,
118              struct GNUNET_TIME_Absolute min_expiration,
119              const char *msg)
120 {
121   struct GNUNET_FS_PublishContext *pc = cls;
122   struct GNUNET_FS_ProgressInfo pi;
123
124   pc->qre = NULL;
125   if (GNUNET_SYSERR == success)
126   {
127     GNUNET_asprintf (&pc->fi_pos->emsg,
128                      _ ("Publishing failed: %s"),
129                      msg);
130     pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
131     pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
132     pi.value.publish.specifics.error.message = pc->fi_pos->emsg;
133     pc->fi_pos->client_info =
134       GNUNET_FS_publish_make_status_ (&pi, pc, pc->fi_pos, 0);
135     if ((GNUNET_YES != pc->fi_pos->is_directory) &&
136         (NULL != pc->fi_pos->filename) &&
137         (GNUNET_YES == pc->any_done) &&
138         (GNUNET_YES == pc->fi_pos->data.file.do_index))
139     {
140       /* run unindex to clean up */
141       GNUNET_FS_unindex_start (pc->h,
142                                pc->fi_pos->filename,
143                                NULL);
144     }
145     return;
146   }
147   pc->any_done = GNUNET_YES;
148   GNUNET_assert (NULL == pc->upload_task);
149   pc->upload_task =
150     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
151                                         &GNUNET_FS_publish_main_, pc);
152 }
153
154
155 /**
156  * Generate the callback that signals clients
157  * that a file (or directory) has been completely
158  * published.
159  *
160  * @param p the completed upload
161  * @param pc context of the publication
162  */
163 static void
164 signal_publish_completion (struct GNUNET_FS_FileInformation *p,
165                            struct GNUNET_FS_PublishContext *pc)
166 {
167   struct GNUNET_FS_ProgressInfo pi;
168
169   pi.status = GNUNET_FS_STATUS_PUBLISH_COMPLETED;
170   pi.value.publish.eta = GNUNET_TIME_UNIT_ZERO;
171   pi.value.publish.specifics.completed.chk_uri = p->chk_uri;
172   pi.value.publish.specifics.completed.sks_uri = p->sks_uri;
173   p->client_info =
174     GNUNET_FS_publish_make_status_ (&pi, pc, p,
175                                     p->data.file.file_size);
176 }
177
178
179 /**
180  * Generate the callback that signals clients
181  * that a file (or directory) has encountered
182  * a problem during publication.
183  *
184  * @param p the upload that had trouble
185  * @param pc context of the publication
186  * @param emsg error message
187  */
188 static void
189 signal_publish_error (struct GNUNET_FS_FileInformation *p,
190                       struct GNUNET_FS_PublishContext *pc,
191                       const char *emsg)
192 {
193   struct GNUNET_FS_ProgressInfo pi;
194
195   p->emsg = GNUNET_strdup (emsg);
196   pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
197   pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
198   pi.value.publish.specifics.error.message = emsg;
199   p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
200   if ((p->is_directory != GNUNET_YES) &&
201       (NULL != p->filename) &&
202       (GNUNET_YES == pc->any_done) &&
203       (p->data.file.do_index == GNUNET_YES))
204   {
205     /* run unindex to clean up */
206     GNUNET_FS_unindex_start (pc->h,
207                              p->filename,
208                              NULL);
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 :
540                           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                           &ds_put_cont,
551                           pc);
552 }
553
554
555 /**
556  * Function called with information about our
557  * progress in computing the tree encoding.
558  *
559  * @param cls closure
560  * @param offset where are we in the file
561  * @param pt_block plaintext of the currently processed block
562  * @param pt_size size of @a pt_block
563  * @param depth depth of the block in the tree, 0 for DBLOCK
564  */
565 static void
566 progress_proc (void *cls, uint64_t offset,
567                const void *pt_block,
568                size_t pt_size,
569                unsigned int depth)
570 {
571   struct GNUNET_FS_PublishContext *pc = cls;
572   struct GNUNET_FS_FileInformation *p;
573   struct GNUNET_FS_FileInformation *par;
574   struct GNUNET_FS_ProgressInfo pi;
575
576   p = pc->fi_pos;
577   pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
578   pi.value.publish.specifics.progress.data = pt_block;
579   pi.value.publish.specifics.progress.offset = offset;
580   pi.value.publish.specifics.progress.data_len = pt_size;
581   pi.value.publish.specifics.progress.depth = depth;
582   p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, offset);
583   if ((0 != depth) ||
584       (GNUNET_YES == p->is_directory))
585     return;
586   while (NULL != (par = p->dir))
587   {
588     p = par;
589     GNUNET_assert (GNUNET_YES == par->is_directory);
590     p->data.dir.contents_completed += pt_size;
591     pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY;
592     pi.value.publish.specifics.progress_directory.completed =
593       p->data.dir.contents_completed;
594     pi.value.publish.specifics.progress_directory.total =
595       p->data.dir.contents_size;
596     pi.value.publish.specifics.progress_directory.eta =
597       GNUNET_TIME_calculate_eta (p->start_time,
598                                  p
599                                  ->data.dir.contents_completed,
600                                  p
601                                  ->data.dir.contents_size);
602     p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
603   }
604 }
605
606
607 /**
608  * We are uploading a file or directory; load (if necessary) the next
609  * block into memory, encrypt it and send it to the FS service.  Then
610  * continue with the main task.
611  *
612  * @param pc overall upload data
613  */
614 static void
615 publish_content (struct GNUNET_FS_PublishContext *pc)
616 {
617   struct GNUNET_FS_FileInformation *p;
618   char *emsg;
619   struct GNUNET_FS_DirectoryBuilder *db;
620   struct GNUNET_FS_FileInformation *dirpos;
621   void *raw_data;
622   uint64_t size;
623
624   p = pc->fi_pos;
625   GNUNET_assert (NULL != p);
626   if (NULL == p->te)
627   {
628     if (GNUNET_YES == p->is_directory)
629     {
630       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating directory\n");
631       db = GNUNET_FS_directory_builder_create (p->meta);
632       dirpos = p->data.dir.entries;
633       while (NULL != dirpos)
634       {
635         if (GNUNET_YES == dirpos->is_directory)
636         {
637           raw_data = dirpos->data.dir.dir_data;
638           dirpos->data.dir.dir_data = NULL;
639         }
640         else
641         {
642           raw_data = NULL;
643           if ((dirpos->data.file.file_size < MAX_INLINE_SIZE) &&
644               (dirpos->data.file.file_size > 0))
645           {
646             raw_data = GNUNET_malloc (dirpos->data.file.file_size);
647             emsg = NULL;
648             if (dirpos->data.file.file_size !=
649                 dirpos->data.file.reader (dirpos->data.file.reader_cls, 0,
650                                           dirpos->data.file.file_size, raw_data,
651                                           &emsg))
652             {
653               GNUNET_free_non_null (emsg);
654               GNUNET_free (raw_data);
655               raw_data = NULL;
656             }
657             dirpos->data.file.reader (dirpos->data.file.reader_cls, UINT64_MAX,
658                                       0, 0, NULL);
659           }
660         }
661         GNUNET_FS_directory_builder_add (db, dirpos->chk_uri, dirpos->meta,
662                                          raw_data);
663         GNUNET_free_non_null (raw_data);
664         dirpos = dirpos->next;
665       }
666       GNUNET_free_non_null (p->data.dir.dir_data);
667       p->data.dir.dir_data = NULL;
668       p->data.dir.dir_size = 0;
669       GNUNET_FS_directory_builder_finish (db, &p->data.dir.dir_size,
670                                           &p->data.dir.dir_data);
671       GNUNET_FS_file_information_sync_ (p);
672     }
673     size = (GNUNET_YES == p->is_directory) ? p->data.dir.dir_size :
674            p->data.file.file_size;
675     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
676                 "Creating tree encoder\n");
677     p->te =
678       GNUNET_FS_tree_encoder_create (pc->h, size, pc, &block_reader,
679                                      &block_proc, &progress_proc,
680                                      &encode_cont);
681   }
682   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
683               "Processing next block from tree\n");
684   GNUNET_FS_tree_encoder_next (p->te);
685 }
686
687
688 /**
689  * Check the response from the "fs" service to our 'start index'
690  * request.
691  *
692  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
693  * @param msg the response we got
694  */
695 static int
696 check_index_start_failed (void *cls,
697                           const struct GNUNET_MessageHeader *msg)
698 {
699   size_t msize = ntohs (msg->size) - sizeof(*msg);
700   const char *emsg = (const char *) &msg[1];
701
702   if (emsg[msize - sizeof(struct GNUNET_MessageHeader) - 1] != '\0')
703   {
704     GNUNET_break (0);
705     return GNUNET_SYSERR;
706   }
707   return GNUNET_OK;
708 }
709
710
711 /**
712  * Process the response from the "fs" service to our 'start index'
713  * request.
714  *
715  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
716  * @param msg the response we got
717  */
718 static void
719 handle_index_start_failed (void *cls,
720                            const struct GNUNET_MessageHeader *msg)
721 {
722   struct GNUNET_FS_PublishContext *pc = cls;
723   struct GNUNET_FS_FileInformation *p;
724   const char *emsg = (const char *) &msg[1];
725   char *msgtxt;
726
727   GNUNET_MQ_destroy (pc->mq);
728   pc->mq = NULL;
729   p = pc->fi_pos;
730   GNUNET_asprintf (&msgtxt,
731                    _ ("Can not index file `%s': %s.\n"),
732                    p->filename,
733                    gettext (emsg));
734   signal_publish_error (p,
735                         pc,
736                         msgtxt);
737   GNUNET_free (msgtxt);
738   GNUNET_FS_file_information_sync_ (p);
739   GNUNET_FS_publish_sync_ (pc);
740 }
741
742
743 /**
744  * Process the response from the "fs" service to our 'start index'
745  * request.
746  *
747  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
748  * @param msg the response we got
749  */
750 static void
751 handle_index_start_ok (void *cls,
752                        const struct GNUNET_MessageHeader *msg)
753 {
754   struct GNUNET_FS_PublishContext *pc = cls;
755   struct GNUNET_FS_FileInformation *p;
756
757   GNUNET_MQ_destroy (pc->mq);
758   pc->mq = NULL;
759   p = pc->fi_pos;
760   p->data.file.index_start_confirmed = GNUNET_YES;
761   GNUNET_FS_file_information_sync_ (p);
762   publish_content (pc);
763 }
764
765
766 /**
767  * Generic error handler, called with the appropriate error code and
768  * the same closure specified at the creation of the message queue.
769  * Not every message queue implementation supports an error handler.
770  *
771  * @param cls closure with the `struct GNUNET_FS_PublishContext *`
772  * @param error error code
773  */
774 static void
775 index_mq_error_handler (void *cls,
776                         enum GNUNET_MQ_Error error)
777 {
778   struct GNUNET_FS_PublishContext *pc = cls;
779   struct GNUNET_FS_FileInformation *p;
780
781   if (NULL != pc->mq)
782   {
783     GNUNET_MQ_destroy (pc->mq);
784     pc->mq = NULL;
785   }
786   p = pc->fi_pos;
787   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
788               _ ("Can not index file `%s': %s.  Will try to insert instead.\n"),
789               p->filename,
790               _ ("error on index-start request to `fs' service"));
791   p->data.file.do_index = GNUNET_NO;
792   GNUNET_FS_file_information_sync_ (p);
793   publish_content (pc);
794 }
795
796
797 /**
798  * Function called once the hash computation over an
799  * indexed file has completed.
800  *
801  * @param cls closure, our publishing context
802  * @param res resulting hash, NULL on error
803  */
804 static void
805 hash_for_index_cb (void *cls,
806                    const struct GNUNET_HashCode *res)
807 {
808   struct GNUNET_FS_PublishContext *pc = cls;
809   struct GNUNET_MQ_MessageHandler handlers[] = {
810     GNUNET_MQ_hd_fixed_size (index_start_ok,
811                              GNUNET_MESSAGE_TYPE_FS_INDEX_START_OK,
812                              struct GNUNET_MessageHeader,
813                              pc),
814     GNUNET_MQ_hd_var_size (index_start_failed,
815                            GNUNET_MESSAGE_TYPE_FS_INDEX_START_FAILED,
816                            struct GNUNET_MessageHeader,
817                            pc),
818     GNUNET_MQ_handler_end ()
819   };
820   struct GNUNET_FS_FileInformation *p;
821   struct GNUNET_MQ_Envelope *env;
822   struct IndexStartMessage *ism;
823   size_t slen;
824   uint64_t dev;
825   uint64_t ino;
826   char *fn;
827
828   pc->fhc = NULL;
829   p = pc->fi_pos;
830   if (NULL == res)
831   {
832     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
833                 _ (
834                   "Can not index file `%s': %s.  Will try to insert instead.\n"),
835                 p->filename,
836                 _ ("failed to compute hash"));
837     p->data.file.do_index = GNUNET_NO;
838     GNUNET_FS_file_information_sync_ (p);
839     publish_content (pc);
840     return;
841   }
842   if (GNUNET_YES == p->data.file.index_start_confirmed)
843   {
844     publish_content (pc);
845     return;
846   }
847   fn = GNUNET_STRINGS_filename_expand (p->filename);
848   GNUNET_assert (fn != NULL);
849   slen = strlen (fn) + 1;
850   if (slen >=
851       GNUNET_MAX_MESSAGE_SIZE - sizeof(struct IndexStartMessage))
852   {
853     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
854                 _
855                   ("Can not index file `%s': %s.  Will try to insert instead.\n"),
856                 fn, _ ("filename too long"));
857     GNUNET_free (fn);
858     p->data.file.do_index = GNUNET_NO;
859     GNUNET_FS_file_information_sync_ (p);
860     publish_content (pc);
861     return;
862   }
863   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
864               "Hash of indexed file `%s' is `%s'\n",
865               p->filename,
866               GNUNET_h2s (res));
867   if (0 != (pc->options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
868   {
869     p->data.file.file_id = *res;
870     p->data.file.have_hash = GNUNET_YES;
871     p->data.file.index_start_confirmed = GNUNET_YES;
872     GNUNET_FS_file_information_sync_ (p);
873     publish_content (pc);
874     GNUNET_free (fn);
875     return;
876   }
877   pc->mq = GNUNET_CLIENT_connect (pc->h->cfg,
878                                   "fs",
879                                   handlers,
880                                   &index_mq_error_handler,
881                                   pc);
882   if (NULL == pc->mq)
883   {
884     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
885                 _ (
886                   "Can not index file `%s': %s.  Will try to insert instead.\n"),
887                 p->filename,
888                 _ ("could not connect to `fs' service"));
889     p->data.file.do_index = GNUNET_NO;
890     publish_content (pc);
891     GNUNET_free (fn);
892     return;
893   }
894   if (p->data.file.have_hash != GNUNET_YES)
895   {
896     p->data.file.file_id = *res;
897     p->data.file.have_hash = GNUNET_YES;
898     GNUNET_FS_file_information_sync_ (p);
899   }
900   env = GNUNET_MQ_msg_extra (ism,
901                              slen,
902                              GNUNET_MESSAGE_TYPE_FS_INDEX_START);
903   if (GNUNET_OK ==
904       GNUNET_DISK_file_get_identifiers (p->filename,
905                                         &dev,
906                                         &ino))
907   {
908     ism->device = GNUNET_htonll (dev);
909     ism->inode = GNUNET_htonll (ino);
910   }
911   else
912   {
913     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
914                 _ ("Failed to get file identifiers for `%s'\n"),
915                 p->filename);
916   }
917   ism->file_id = *res;
918   GNUNET_memcpy (&ism[1],
919                  fn,
920                  slen);
921   GNUNET_free (fn);
922   GNUNET_MQ_send (pc->mq,
923                   env);
924 }
925
926
927 /**
928  * We've computed the CHK/LOC URI, now publish the KSKs (if applicable).
929  *
930  * @param pc publishing context to do this for
931  */
932 static void
933 publish_kblocks (struct GNUNET_FS_PublishContext *pc)
934 {
935   struct GNUNET_FS_FileInformation *p;
936
937   p = pc->fi_pos;
938   /* upload of "p" complete, publish KBlocks! */
939   if (NULL != p->keywords)
940   {
941     pc->ksk_pc = GNUNET_FS_publish_ksk (pc->h,
942                                         p->keywords,
943                                         p->meta,
944                                         p->chk_uri,
945                                         &p->bo,
946                                         pc->options,
947                                         &publish_kblocks_cont,
948                                         pc);
949   }
950   else
951   {
952     publish_kblocks_cont (pc, p->chk_uri, NULL);
953   }
954 }
955
956
957 /**
958  * Process the response from the "fs" service to our LOC sign request.
959  *
960  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
961  * @param sig the response we got
962  */
963 static void
964 handle_signature_response (void *cls,
965                            const struct ResponseLocSignatureMessage *sig)
966 {
967   struct GNUNET_FS_PublishContext *pc = cls;
968   struct GNUNET_FS_FileInformation *p;
969
970   p = pc->fi_pos;
971   p->chk_uri->type = GNUNET_FS_URI_LOC;
972   /* p->data.loc.fi kept from CHK before */
973   p->chk_uri->data.loc.peer = sig->peer;
974   p->chk_uri->data.loc.expirationTime
975     = GNUNET_TIME_absolute_ntoh (sig->expiration_time);
976   p->chk_uri->data.loc.contentSignature = sig->signature;
977   GNUNET_FS_file_information_sync_ (p);
978   GNUNET_FS_publish_sync_ (pc);
979   publish_kblocks (pc);
980 }
981
982
983 /**
984  * Generic error handler, called with the appropriate error code and
985  * the same closure specified at the creation of the message queue.
986  * Not every message queue implementation supports an error handler.
987  *
988  * @param cls closure with the `struct GNUNET_FS_PublishContext *`
989  * @param error error code
990  */
991 static void
992 loc_mq_error_handler (void *cls,
993                       enum GNUNET_MQ_Error error)
994 {
995   struct GNUNET_FS_PublishContext *pc = cls;
996
997   if (NULL != pc->mq)
998   {
999     GNUNET_MQ_destroy (pc->mq);
1000     pc->mq = NULL;
1001   }
1002   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1003               _ ("Can not create LOC URI. Will continue with CHK instead.\n"));
1004   publish_kblocks (pc);
1005 }
1006
1007
1008 /**
1009  * We're publishing without anonymity. Contact the FS service
1010  * to create a signed LOC URI for further processing, then
1011  * continue with KSKs.
1012  *
1013  * @param pc the publishing context do to this for
1014  */
1015 static void
1016 create_loc_uri (struct GNUNET_FS_PublishContext *pc)
1017 {
1018   struct GNUNET_MQ_MessageHandler handlers[] = {
1019     GNUNET_MQ_hd_fixed_size (signature_response,
1020                              GNUNET_MESSAGE_TYPE_FS_REQUEST_LOC_SIGNATURE,
1021                              struct ResponseLocSignatureMessage,
1022                              pc),
1023     GNUNET_MQ_handler_end ()
1024   };
1025   struct GNUNET_MQ_Envelope *env;
1026   struct RequestLocSignatureMessage *req;
1027   struct GNUNET_FS_FileInformation *p;
1028
1029   if (NULL != pc->mq)
1030     GNUNET_MQ_destroy (pc->mq);
1031   pc->mq = GNUNET_CLIENT_connect (pc->h->cfg,
1032                                   "fs",
1033                                   handlers,
1034                                   &loc_mq_error_handler,
1035                                   pc);
1036   if (NULL == pc->mq)
1037   {
1038     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1039                 _ (
1040                   "Can not create LOC URI. Will continue with CHK instead.\n"));
1041     publish_kblocks (pc);
1042     return;
1043   }
1044   p = pc->fi_pos;
1045   env = GNUNET_MQ_msg (req,
1046                        GNUNET_MESSAGE_TYPE_FS_REQUEST_LOC_SIGN);
1047   req->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT);
1048   req->expiration_time = GNUNET_TIME_absolute_hton (p->bo.expiration_time);
1049   req->chk = p->chk_uri->data.chk.chk;
1050   req->file_length = GNUNET_htonll (p->chk_uri->data.chk.file_length);
1051   GNUNET_MQ_send (pc->mq,
1052                   env);
1053 }
1054
1055
1056 /**
1057  * Main function that performs the upload.
1058  *
1059  * @param cls `struct GNUNET_FS_PublishContext *` identifies the upload
1060  */
1061 void
1062 GNUNET_FS_publish_main_ (void *cls)
1063 {
1064   struct GNUNET_FS_PublishContext *pc = cls;
1065   struct GNUNET_FS_ProgressInfo pi;
1066   struct GNUNET_FS_FileInformation *p;
1067   char *fn;
1068
1069   pc->upload_task = NULL;
1070   p = pc->fi_pos;
1071   if (NULL == p)
1072   {
1073     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1074                 "Publishing complete, now publishing SKS and KSK blocks.\n");
1075     /* upload of entire hierarchy complete,
1076      * publish namespace entries */
1077     GNUNET_FS_publish_sync_ (pc);
1078     publish_sblock (pc);
1079     return;
1080   }
1081   /* find starting position */
1082   while ((GNUNET_YES == p->is_directory) &&
1083          (NULL != p->data.dir.entries) &&
1084          (NULL == p->emsg) &&
1085          (NULL == p->data.dir.entries->chk_uri))
1086   {
1087     p = p->data.dir.entries;
1088     pc->fi_pos = p;
1089     GNUNET_FS_publish_sync_ (pc);
1090   }
1091   /* abort on error */
1092   if (NULL != p->emsg)
1093   {
1094     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1095                 "Error uploading: %s\n",
1096                 p->emsg);
1097     /* error with current file, abort all
1098      * related files as well! */
1099     while (NULL != p->dir)
1100     {
1101       fn = GNUNET_CONTAINER_meta_data_get_by_type (p->meta,
1102                                                    EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
1103       p = p->dir;
1104       if (fn != NULL)
1105       {
1106         GNUNET_asprintf (&p->emsg,
1107                          _ ("Recursive upload failed at `%s': %s"),
1108                          fn,
1109                          p->emsg);
1110         GNUNET_free (fn);
1111       }
1112       else
1113       {
1114         GNUNET_asprintf (&p->emsg,
1115                          _ ("Recursive upload failed: %s"),
1116                          p->emsg);
1117       }
1118       pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
1119       pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
1120       pi.value.publish.specifics.error.message = p->emsg;
1121       p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
1122     }
1123     pc->all_done = GNUNET_YES;
1124     GNUNET_FS_publish_sync_ (pc);
1125     return;
1126   }
1127   /* handle completion */
1128   if (NULL != p->chk_uri)
1129   {
1130     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1131                 "File upload complete, now publishing KSK blocks.\n");
1132     GNUNET_FS_publish_sync_ (pc);
1133
1134     if ((0 == p->bo.anonymity_level) &&
1135         (GNUNET_YES !=
1136          GNUNET_FS_uri_test_loc (p->chk_uri)))
1137     {
1138       /* zero anonymity, box CHK URI in LOC URI */
1139       create_loc_uri (pc);
1140     }
1141     else
1142     {
1143       publish_kblocks (pc);
1144     }
1145     return;
1146   }
1147   if ((GNUNET_YES != p->is_directory) && (p->data.file.do_index))
1148   {
1149     if (NULL == p->filename)
1150     {
1151       p->data.file.do_index = GNUNET_NO;
1152       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1153                   _ (
1154                     "Can not index file `%s': %s.  Will try to insert instead.\n"),
1155                   "<no-name>",
1156                   _ ("needs to be an actual file"));
1157       GNUNET_FS_file_information_sync_ (p);
1158       publish_content (pc);
1159       return;
1160     }
1161     if (p->data.file.have_hash)
1162     {
1163       hash_for_index_cb (pc, &p->data.file.file_id);
1164     }
1165     else
1166     {
1167       p->start_time = GNUNET_TIME_absolute_get ();
1168       pc->fhc =
1169         GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE, p->filename,
1170                                  HASHING_BLOCKSIZE, &hash_for_index_cb, pc);
1171     }
1172     return;
1173   }
1174   publish_content (pc);
1175 }
1176
1177
1178 /**
1179  * Signal the FS's progress function that we are starting
1180  * an upload.
1181  *
1182  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
1183  * @param fi the entry in the publish-structure
1184  * @param length length of the file or directory
1185  * @param meta metadata for the file or directory (can be modified)
1186  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1187  * @param bo block options
1188  * @param do_index should we index?
1189  * @param client_info pointer to client context set upon creation (can be modified)
1190  * @return #GNUNET_OK to continue (always)
1191  */
1192 static int
1193 fip_signal_start (void *cls,
1194                   struct GNUNET_FS_FileInformation *fi,
1195                   uint64_t length,
1196                   struct GNUNET_CONTAINER_MetaData *meta,
1197                   struct GNUNET_FS_Uri **uri,
1198                   struct GNUNET_FS_BlockOptions *bo,
1199                   int *do_index,
1200                   void **client_info)
1201 {
1202   struct GNUNET_FS_PublishContext *pc = cls;
1203   struct GNUNET_FS_ProgressInfo pi;
1204   unsigned int kc;
1205   uint64_t left;
1206
1207   if (GNUNET_YES == pc->skip_next_fi_callback)
1208   {
1209     pc->skip_next_fi_callback = GNUNET_NO;
1210     return GNUNET_OK;
1211   }
1212   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1213               "Starting publish operation\n");
1214   if (*do_index)
1215   {
1216     /* space for on-demand blocks */
1217     pc->reserve_space +=
1218       ((length + DBLOCK_SIZE
1219         - 1) / DBLOCK_SIZE) * sizeof(struct OnDemandBlock);
1220   }
1221   else
1222   {
1223     /* space for DBlocks */
1224     pc->reserve_space += length;
1225   }
1226   /* entries for IBlocks and DBlocks, space for IBlocks */
1227   left = length;
1228   while (1)
1229   {
1230     left = (left + DBLOCK_SIZE - 1) / DBLOCK_SIZE;
1231     pc->reserve_entries += left;
1232     if (left <= 1)
1233       break;
1234     left = left * sizeof(struct ContentHashKey);
1235     pc->reserve_space += left;
1236   }
1237   pc->reserve_entries++;
1238   /* entries and space for keywords */
1239   if (NULL != *uri)
1240   {
1241     kc = GNUNET_FS_uri_ksk_get_keyword_count (*uri);
1242     pc->reserve_entries += kc;
1243     pc->reserve_space += GNUNET_MAX_MESSAGE_SIZE * kc;
1244   }
1245   pi.status = GNUNET_FS_STATUS_PUBLISH_START;
1246   *client_info = GNUNET_FS_publish_make_status_ (&pi, pc, fi, 0);
1247   GNUNET_FS_file_information_sync_ (fi);
1248   if ((fi->is_directory) && (fi->dir != NULL))
1249   {
1250     /* We are a directory, and we are not top-level; process entries in directory */
1251     pc->skip_next_fi_callback = GNUNET_YES;
1252     GNUNET_FS_file_information_inspect (fi, &fip_signal_start, pc);
1253   }
1254   return GNUNET_OK;
1255 }
1256
1257
1258 /**
1259  * Actually signal the FS's progress function that we are suspending
1260  * an upload.
1261  *
1262  * @param fi the entry in the publish-structure
1263  * @param pc the publish context of which a file is being suspended
1264  */
1265 static void
1266 suspend_operation (struct GNUNET_FS_FileInformation *fi,
1267                    struct GNUNET_FS_PublishContext *pc)
1268 {
1269   struct GNUNET_FS_ProgressInfo pi;
1270   uint64_t off;
1271
1272   if (NULL != pc->ksk_pc)
1273   {
1274     GNUNET_FS_publish_ksk_cancel (pc->ksk_pc);
1275     pc->ksk_pc = NULL;
1276   }
1277   if (NULL != pc->sks_pc)
1278   {
1279     GNUNET_FS_publish_sks_cancel (pc->sks_pc);
1280     pc->sks_pc = NULL;
1281   }
1282   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1283               "Suspending publish operation\n");
1284   GNUNET_free_non_null (fi->serialization);
1285   fi->serialization = NULL;
1286   off = (NULL == fi->chk_uri) ? 0 : (GNUNET_YES == fi->is_directory) ?
1287         fi->data.dir.dir_size : fi->data.file.file_size;
1288   pi.status = GNUNET_FS_STATUS_PUBLISH_SUSPEND;
1289   GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, fi, off));
1290   if (NULL != pc->qre)
1291   {
1292     GNUNET_DATASTORE_cancel (pc->qre);
1293     pc->qre = NULL;
1294   }
1295   if (NULL != pc->dsh)
1296   {
1297     GNUNET_DATASTORE_disconnect (pc->dsh, GNUNET_NO);
1298     pc->dsh = NULL;
1299   }
1300   pc->rid = 0;
1301 }
1302
1303
1304 /**
1305  * Signal the FS's progress function that we are suspending
1306  * an upload.  Performs the recursion.
1307  *
1308  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
1309  * @param fi the entry in the publish-structure
1310  * @param length length of the file or directory
1311  * @param meta metadata for the file or directory (can be modified)
1312  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1313  * @param bo block options
1314  * @param do_index should we index?
1315  * @param client_info pointer to client context set upon creation (can be modified)
1316  * @return #GNUNET_OK to continue (always)
1317  */
1318 static int
1319 fip_signal_suspend (void *cls,
1320                     struct GNUNET_FS_FileInformation *fi,
1321                     uint64_t length,
1322                     struct GNUNET_CONTAINER_MetaData *meta,
1323                     struct GNUNET_FS_Uri **uri,
1324                     struct GNUNET_FS_BlockOptions *bo,
1325                     int *do_index,
1326                     void **client_info)
1327 {
1328   struct GNUNET_FS_PublishContext *pc = cls;
1329
1330   if (GNUNET_YES == pc->skip_next_fi_callback)
1331   {
1332     pc->skip_next_fi_callback = GNUNET_NO;
1333     return GNUNET_OK;
1334   }
1335   if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta))
1336   {
1337     /* process entries in directory */
1338     pc->skip_next_fi_callback = GNUNET_YES;
1339     GNUNET_FS_file_information_inspect (fi, &fip_signal_suspend, pc);
1340   }
1341   suspend_operation (fi, pc);
1342   *client_info = NULL;
1343   return GNUNET_OK;
1344 }
1345
1346
1347 /**
1348  * Create SUSPEND event for the given publish operation
1349  * and then clean up our state (without stop signal).
1350  *
1351  * @param cls the `struct GNUNET_FS_PublishContext` to signal for
1352  */
1353 void
1354 GNUNET_FS_publish_signal_suspend_ (void *cls)
1355 {
1356   struct GNUNET_FS_PublishContext *pc = cls;
1357
1358   if (NULL != pc->upload_task)
1359   {
1360     GNUNET_SCHEDULER_cancel (pc->upload_task);
1361     pc->upload_task = NULL;
1362   }
1363   pc->skip_next_fi_callback = GNUNET_YES;
1364   GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_suspend, pc);
1365   suspend_operation (pc->fi, pc);
1366   GNUNET_FS_end_top (pc->h, pc->top);
1367   pc->top = NULL;
1368   publish_cleanup (pc);
1369 }
1370
1371
1372 /**
1373  * We have gotten a reply for our space reservation request.
1374  * Either fail (insufficient space) or start publishing for good.
1375  *
1376  * @param cls the `struct GNUNET_FS_PublishContext *`
1377  * @param success positive reservation ID on success
1378  * @param min_expiration minimum expiration time required for content to be stored
1379  * @param msg error message on error, otherwise NULL
1380  */
1381 static void
1382 finish_reserve (void *cls,
1383                 int success,
1384                 struct GNUNET_TIME_Absolute min_expiration,
1385                 const char *msg)
1386 {
1387   struct GNUNET_FS_PublishContext *pc = cls;
1388
1389   pc->qre = NULL;
1390   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1391               "Reservation complete (%d)!\n",
1392               success);
1393   if ((msg != NULL) || (success <= 0))
1394   {
1395     GNUNET_asprintf (&pc->fi->emsg,
1396                      _ ("Datastore failure: %s"),
1397                      msg);
1398     signal_publish_error (pc->fi, pc, pc->fi->emsg);
1399     return;
1400   }
1401   pc->rid = success;
1402   GNUNET_assert (NULL == pc->upload_task);
1403   pc->upload_task =
1404     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
1405                                         &GNUNET_FS_publish_main_, pc);
1406 }
1407
1408
1409 /**
1410  * Calculate the total size of all of the files in the directory structure.
1411  *
1412  * @param fi file structure to traverse
1413  */
1414 static uint64_t
1415 compute_contents_size (struct GNUNET_FS_FileInformation *fi)
1416 {
1417   struct GNUNET_FS_FileInformation *ent;
1418
1419   if (GNUNET_YES != fi->is_directory)
1420     return fi->data.file.file_size;
1421   fi->data.dir.contents_size = 0;
1422   for (ent = fi->data.dir.entries; NULL != ent; ent = ent->next)
1423     fi->data.dir.contents_size += compute_contents_size (ent);
1424   return fi->data.dir.contents_size;
1425 }
1426
1427
1428 /**
1429  * Publish a file or directory.
1430  *
1431  * @param h handle to the file sharing subsystem
1432  * @param fi information about the file or directory structure to publish
1433  * @param ns namespace to publish the file in, NULL for no namespace
1434  * @param nid identifier to use for the publishd content in the namespace
1435  *        (can be NULL, must be NULL if namespace is NULL)
1436  * @param nuid update-identifier that will be used for future updates
1437  *        (can be NULL, must be NULL if namespace or nid is NULL)
1438  * @param options options for the publication
1439  * @return context that can be used to control the publish operation
1440  */
1441 struct GNUNET_FS_PublishContext *
1442 GNUNET_FS_publish_start (struct GNUNET_FS_Handle *h,
1443                          struct GNUNET_FS_FileInformation *fi,
1444                          const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns,
1445                          const char *nid,
1446                          const char *nuid,
1447                          enum GNUNET_FS_PublishOptions options)
1448 {
1449   struct GNUNET_FS_PublishContext *ret;
1450   struct GNUNET_DATASTORE_Handle *dsh;
1451
1452   GNUNET_assert (NULL != h);
1453   compute_contents_size (fi);
1454   if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1455   {
1456     dsh = GNUNET_DATASTORE_connect (h->cfg);
1457     if (NULL == dsh)
1458       return NULL;
1459   }
1460   else
1461   {
1462     dsh = NULL;
1463   }
1464   ret = GNUNET_new (struct GNUNET_FS_PublishContext);
1465   ret->dsh = dsh;
1466   ret->h = h;
1467   ret->fi = fi;
1468   if (NULL != ns)
1469   {
1470     ret->ns = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey);
1471     *ret->ns = *ns;
1472     GNUNET_assert (NULL != nid);
1473     ret->nid = GNUNET_strdup (nid);
1474     if (NULL != nuid)
1475       ret->nuid = GNUNET_strdup (nuid);
1476   }
1477   ret->options = options;
1478   /* signal start */
1479   GNUNET_FS_file_information_inspect (ret->fi, &fip_signal_start, ret);
1480   ret->fi_pos = ret->fi;
1481   ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_publish_signal_suspend_, ret);
1482   GNUNET_FS_publish_sync_ (ret);
1483   if (NULL != ret->dsh)
1484   {
1485     GNUNET_assert (NULL == ret->qre);
1486     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1487                 _ (
1488                   "Reserving space for %u entries and %llu bytes for publication\n"),
1489                 (unsigned int) ret->reserve_entries,
1490                 (unsigned long long) ret->reserve_space);
1491     ret->qre =
1492       GNUNET_DATASTORE_reserve (ret->dsh, ret->reserve_space,
1493                                 ret->reserve_entries,
1494                                 &finish_reserve,
1495                                 ret);
1496   }
1497   else
1498   {
1499     GNUNET_assert (NULL == ret->upload_task);
1500     ret->upload_task =
1501       GNUNET_SCHEDULER_add_with_priority
1502         (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, &GNUNET_FS_publish_main_, ret);
1503   }
1504   return ret;
1505 }
1506
1507
1508 /**
1509  * Signal the FS's progress function that we are stopping
1510  * an upload.
1511  *
1512  * @param cls closure (of type `struct GNUNET_FS_PublishContext *`)
1513  * @param fi the entry in the publish-structure
1514  * @param length length of the file or directory
1515  * @param meta metadata for the file or directory (can be modified)
1516  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1517  * @param bo block options (can be modified)
1518  * @param do_index should we index?
1519  * @param client_info pointer to client context set upon creation (can be modified)
1520  * @return #GNUNET_OK to continue (always)
1521  */
1522 static int
1523 fip_signal_stop (void *cls,
1524                  struct GNUNET_FS_FileInformation *fi,
1525                  uint64_t length,
1526                  struct GNUNET_CONTAINER_MetaData *meta,
1527                  struct GNUNET_FS_Uri **uri,
1528                  struct GNUNET_FS_BlockOptions *bo,
1529                  int *do_index, void **client_info)
1530 {
1531   struct GNUNET_FS_PublishContext *pc = cls;
1532   struct GNUNET_FS_ProgressInfo pi;
1533   uint64_t off;
1534
1535   if (GNUNET_YES == pc->skip_next_fi_callback)
1536   {
1537     pc->skip_next_fi_callback = GNUNET_NO;
1538     return GNUNET_OK;
1539   }
1540   if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta))
1541   {
1542     /* process entries in directory first */
1543     pc->skip_next_fi_callback = GNUNET_YES;
1544     GNUNET_FS_file_information_inspect (fi, &fip_signal_stop, pc);
1545   }
1546   if (NULL != fi->serialization)
1547   {
1548     GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_FILE_INFO,
1549                                  fi->serialization);
1550     GNUNET_free (fi->serialization);
1551     fi->serialization = NULL;
1552   }
1553   off = (fi->chk_uri == NULL) ? 0 : length;
1554   pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
1555   GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, fi, off));
1556   *client_info = NULL;
1557   return GNUNET_OK;
1558 }
1559
1560
1561 /**
1562  * Stop an upload.  Will abort incomplete uploads (but
1563  * not remove blocks that have already been publishd) or
1564  * simply clean up the state for completed uploads.
1565  * Must NOT be called from within the event callback!
1566  *
1567  * @param pc context for the upload to stop
1568  */
1569 void
1570 GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *pc)
1571 {
1572   struct GNUNET_FS_ProgressInfo pi;
1573   uint64_t off;
1574
1575   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1576               "Publish stop called\n");
1577   GNUNET_FS_end_top (pc->h, pc->top);
1578   if (NULL != pc->ksk_pc)
1579   {
1580     GNUNET_FS_publish_ksk_cancel (pc->ksk_pc);
1581     pc->ksk_pc = NULL;
1582   }
1583   if (NULL != pc->sks_pc)
1584   {
1585     GNUNET_FS_publish_sks_cancel (pc->sks_pc);
1586     pc->sks_pc = NULL;
1587   }
1588   if (NULL != pc->upload_task)
1589   {
1590     GNUNET_SCHEDULER_cancel (pc->upload_task);
1591     pc->upload_task = NULL;
1592   }
1593   pc->skip_next_fi_callback = GNUNET_YES;
1594   GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_stop, pc);
1595
1596   if (NULL != pc->fi->serialization)
1597   {
1598     GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_FILE_INFO,
1599                                  pc->fi->serialization);
1600     GNUNET_free (pc->fi->serialization);
1601     pc->fi->serialization = NULL;
1602   }
1603   off = (NULL == pc->fi->chk_uri) ? 0 : GNUNET_ntohll (
1604     pc->fi->chk_uri->data.chk.file_length);
1605
1606   if (NULL != pc->serialization)
1607   {
1608     GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_MASTER_PUBLISH,
1609                                  pc->serialization);
1610     GNUNET_free (pc->serialization);
1611     pc->serialization = NULL;
1612   }
1613   if (NULL != pc->qre)
1614   {
1615     GNUNET_DATASTORE_cancel (pc->qre);
1616     pc->qre = NULL;
1617   }
1618   pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
1619   GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, pc->fi, off));
1620   publish_cleanup (pc);
1621 }
1622
1623
1624 /* end of fs_publish.c */