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