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