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