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