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