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