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