event generation
[oweals/gnunet.git] / src / fs / fs_publish.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 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 2, 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 http://gnunet.org/encoding.php3
25  * @author Krista Bennett
26  * @author Christian Grothoff
27  *
28  * TODO:
29  * - KBlocks
30  * - SBlocks
31  * - indexing support
32  * - code-sharing with unindex (can wait)
33  * - persistence support (can wait)
34  * - datastore reservation support (optimization)
35  */
36
37 #include "platform.h"
38 #include "gnunet_constants.h"
39 #include "gnunet_util_lib.h"
40 #include "gnunet_fs_service.h"
41 #include "fs.h"
42
43 #define DEBUG_PUBLISH GNUNET_YES
44
45 /**
46  * Main function that performs the upload.
47  * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
48  * @param tc task context
49  */
50 static void
51 do_upload (void *cls,
52            const struct GNUNET_SCHEDULER_TaskContext *tc);
53
54
55 /**
56  * Context for "ds_put_cont".
57  */
58 struct PutContCtx
59 {
60   /**
61    * Publishing context for which the datastore
62    * PUT request was executed.
63    */
64   struct GNUNET_FS_PublishContext *sc;
65
66   /**
67    * Specific file with the block.
68    */
69   struct GNUNET_FS_FileInformation *p;
70
71   /**
72    * Function to run next, if any (can be NULL).
73    */
74   GNUNET_SCHEDULER_Task cont;
75 };
76
77
78 /**
79  * Fill in all of the generic fields for 
80  * a publish event.
81  *
82  * @param pc structure to fill in
83  * @param sc overall publishing context
84  * @param p file information for the file being published
85  */
86 static void
87 make_publish_status (struct GNUNET_FS_ProgressInfo *pi,
88                      struct GNUNET_FS_PublishContext *sc,
89                      const struct GNUNET_FS_FileInformation *p)
90 {
91   pi->value.publish.sc = sc;
92   pi->value.publish.fi = p;
93   pi->value.publish.cctx
94     = p->client_info;
95   pi->value.publish.pctx
96     = (NULL == p->dir) ? NULL : p->dir->client_info;
97   pi->value.publish.size
98     = (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
99   pi->value.publish.eta 
100     = GNUNET_TIME_calculate_eta (p->start_time,
101                                  p->publish_offset,
102                                  pi->value.publish.size);
103   pi->value.publish.duration = GNUNET_TIME_absolute_get_duration (p->start_time);
104   pi->value.publish.completed = p->publish_offset;
105   pi->value.publish.anonymity = p->anonymity;
106 }
107
108
109 /**
110  * Function called by the datastore API with
111  * the result from the PUT request.
112  *
113  * @param cls our closure
114  * @param success GNUNET_OK on success
115  * @param msg error message (or NULL)
116  */
117 static void
118 ds_put_cont (void *cls,
119              int success,
120              const char *msg)
121 {
122   struct PutContCtx *pcc = cls;
123   struct GNUNET_FS_ProgressInfo pi;
124
125   if (GNUNET_OK != success)
126     {
127       GNUNET_asprintf (&pcc->p->emsg, 
128                        _("Upload failed: %s"),
129                        msg);
130       GNUNET_FS_file_information_sync (pcc->p);
131       pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
132       make_publish_status (&pi, pcc->sc, pcc->p);
133       pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
134       pi.value.publish.specifics.error.message = pcc->p->emsg;
135       pcc->p->client_info
136         = pcc->sc->h->upcb (pcc->sc->h->upcb_cls,
137                             &pi);
138       return;
139     }
140   GNUNET_FS_file_information_sync (pcc->p);
141   if (NULL != pcc->cont)
142     pcc->sc->upload_task 
143       = GNUNET_SCHEDULER_add_delayed (pcc->sc->h->sched,
144                                       GNUNET_NO,
145                                       GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
146                                       GNUNET_SCHEDULER_NO_TASK,
147                                       GNUNET_TIME_UNIT_ZERO,
148                                       pcc->cont,
149                                       pcc->sc);
150   GNUNET_free (pcc);
151 }
152
153
154 /**
155  * We need to publish a specific block.  Do it.  Then continue with
156  * the main task.
157  *
158  * @param sc overall upload data
159  * @param p file that the block belongs to (needed for options!)
160  * @param query what the block should be indexed under
161  * @param blk encoded block to publish
162  * @param blk_size size of the block
163  * @param blk_type type of the block
164  * @param cont function to run when done
165  */
166 static void
167 publish_block (struct GNUNET_FS_PublishContext *sc,
168                struct GNUNET_FS_FileInformation *p,
169                const GNUNET_HashCode *query,
170                const void* blk,
171                uint16_t blk_size,
172                uint32_t blk_type,
173                GNUNET_SCHEDULER_Task cont)
174 {
175   struct PutContCtx * dpc_cls;
176
177   dpc_cls = GNUNET_malloc(sizeof(struct PutContCtx));
178   dpc_cls->cont = cont;
179   dpc_cls->sc = sc;
180   dpc_cls->p = p;
181   // FIXME: need to do something to "sc" to mark
182   // that "sc" can not be freed right now due to this
183   // pending, scheduled operation for which we don't have
184   // a task ID!  
185   GNUNET_DATASTORE_put (sc->dsh,
186                         sc->rid,
187                         query,
188                         blk_size,
189                         blk,
190                         blk_type,
191                         p->priority,
192                         p->anonymity,
193                         p->expirationTime,
194                         GNUNET_CONSTANTS_SERVICE_TIMEOUT,
195                         &ds_put_cont,
196                         dpc_cls);
197 }
198
199
200 /**
201  * Generate the callback that signals clients
202  * that a file (or directory) has been completely
203  * published.
204  *
205  * @param p the completed upload
206  * @param sc context of the publication
207  */
208 static void 
209 signal_publish_completion (struct GNUNET_FS_FileInformation *p,
210                            struct GNUNET_FS_PublishContext *sc)
211 {
212   struct GNUNET_FS_ProgressInfo pi;
213   
214   pi.status = GNUNET_FS_STATUS_PUBLISH_COMPLETED;
215   make_publish_status (&pi, sc, p);
216   pi.value.publish.eta = GNUNET_TIME_UNIT_ZERO;
217   pi.value.publish.specifics.completed.chk_uri = p->chk_uri;
218   p->client_info
219     = sc->h->upcb (sc->h->upcb_cls,
220                   &pi);
221 }
222
223
224 /**
225  * We are almost done publishing the structure,
226  * add SBlocks (if needed).
227  *
228  * @param sc overall upload data
229  */
230 static void
231 publish_sblock (struct GNUNET_FS_PublishContext *sc)
232 {
233   struct GNUNET_FS_FileInformation *p;
234   p = sc->fi;
235
236   // FIXME: build sblock & call publish_block!
237   
238   // FIXME: continuation should
239   // be releasing the datastore reserve
240   // (once implemented)
241   // FIXME: finally, signal overall completion
242   signal_publish_completion (p, sc);
243 }
244
245
246 /**
247  * We have uploaded a file or directory; now publish
248  * the KBlocks in the global keyword space so that
249  * it can be found.  Then continue with the
250  * main task.
251  *
252  * @param sc overall upload data
253  * @param p specific file or directory for which kblocks
254  *          should be created
255  */
256 static void
257 publish_kblocks (struct GNUNET_FS_PublishContext *sc,
258                  struct GNUNET_FS_FileInformation *p)
259 {
260   // FIXME: build all kblocks
261   // call publish_kblock on each
262
263
264   GNUNET_FS_file_information_sync (p);
265   if (NULL != p->dir)
266     signal_publish_completion (p, sc);
267
268   // last continuation should then call the main continuation again
269 }
270
271
272 /**
273  * Compute the depth of the CHK tree.
274  *
275  * @param flen file length for which to compute the depth
276  * @return depth of the tree
277  */
278 static unsigned int
279 compute_depth (uint64_t flen)
280 {
281   unsigned int treeDepth;
282   uint64_t fl;
283
284   treeDepth = 1;
285   fl = GNUNET_FS_DBLOCK_SIZE;
286   while (fl < flen)
287     {
288       treeDepth++;
289       if (fl * GNUNET_FS_CHK_PER_INODE < fl)
290         {
291           /* integer overflow, this is a HUGE file... */
292           return treeDepth;
293         }
294       fl = fl * GNUNET_FS_CHK_PER_INODE;
295     }
296   return treeDepth;
297 }
298
299
300 /**
301  * Compute the size of the current IBlock.
302  *
303  * @param height height of the IBlock in the tree (aka overall
304  *               number of tree levels minus depth); 0 == DBlock
305  * @param offset current offset in the overall file
306  * @return size of the corresponding IBlock
307  */
308 static uint16_t 
309 compute_iblock_size (unsigned int height,
310                      uint64_t offset)
311 {
312   unsigned int ret;
313   unsigned int i;
314   uint64_t mod;
315   uint64_t bds;
316
317   GNUNET_assert (height > 0);
318   bds = GNUNET_FS_DBLOCK_SIZE; /* number of bytes each CHK at level "i"
319                                   corresponds to */
320   for (i=0;i<height;i++)
321     bds *= GNUNET_FS_CHK_PER_INODE;
322   mod = offset % bds;
323   if (0 == mod)
324     {
325       /* we were triggered at the end of a full block */
326       ret = GNUNET_FS_CHK_PER_INODE;
327     }
328   else
329     {
330       /* we were triggered at the end of the file */
331       bds /= GNUNET_FS_CHK_PER_INODE;
332       ret = mod / bds;
333       if (0 != mod % bds)
334         ret++; 
335     }
336   return (uint16_t) (ret * sizeof(struct ContentHashKey));
337 }
338
339
340 /**
341  * Compute the offset of the CHK for the
342  * current block in the IBlock above.
343  *
344  * @param height height of the IBlock in the tree (aka overall
345  *               number of tree levels minus depth); 0 == DBlock
346  * @param offset current offset in the overall file
347  * @return (array of CHKs') offset in the above IBlock
348  */
349 static unsigned int
350 compute_chk_offset (unsigned int height,
351                     uint64_t offset)
352 {
353   uint64_t bds;
354   unsigned int ret;
355   unsigned int i;
356
357   bds = GNUNET_FS_DBLOCK_SIZE; /* number of bytes each CHK at level "i"
358                                   corresponds to */
359   for (i=0;i<height;i++)
360     bds *= GNUNET_FS_CHK_PER_INODE;
361   GNUNET_assert (0 == (offset % bds));
362   ret = offset / bds;
363   return ret % GNUNET_FS_CHK_PER_INODE; 
364 }
365
366
367 /**
368  * We are uploading a file or directory; load (if necessary) the next
369  * block into memory, encrypt it and send it to the FS service.  Then
370  * continue with the main task.
371  *
372  * @param sc overall upload data
373  * @param p specific file or directory for which kblocks
374  *          should be created
375  */
376 static void
377 publish_content (struct GNUNET_FS_PublishContext *sc,
378                  struct GNUNET_FS_FileInformation *p)
379 {
380   struct GNUNET_FS_ProgressInfo pi;
381   struct ContentHashKey *mychk;
382   const void *pt_block;
383   uint16_t pt_size;
384   char *emsg;
385   char iob[GNUNET_FS_DBLOCK_SIZE];
386   char enc[GNUNET_FS_DBLOCK_SIZE];
387   struct GNUNET_CRYPTO_AesSessionKey sk;
388   struct GNUNET_CRYPTO_AesInitializationVector iv;
389   uint64_t size;
390   unsigned int off;
391   struct GNUNET_FS_DirectoryBuilder *db;
392   struct GNUNET_FS_FileInformation *dirpos;
393   void *raw_data;
394   char *dd;
395
396   // FIXME: figure out how to share this code
397   // with unindex!
398   size = (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
399   if (NULL == p->chk_tree)
400     {
401       if (p->is_directory)
402         {
403           db = GNUNET_FS_directory_builder_create (p->meta);
404           dirpos = p->data.dir.entries;
405           while (NULL != dirpos)
406             {
407               if (dirpos->is_directory)
408                 {
409                   raw_data = dirpos->data.dir.dir_data;
410                   dirpos->data.dir.dir_data = NULL;
411                 }
412               else
413                 {
414                   raw_data = NULL;
415                   if ( (dirpos->data.file.file_size < GNUNET_FS_MAX_INLINE_SIZE) &&
416                        (dirpos->data.file.file_size > 0) )
417                     {
418                       raw_data = GNUNET_malloc (dirpos->data.file.file_size);
419                       emsg = NULL;
420                       if (dirpos->data.file.file_size !=
421                           dirpos->data.file.reader (dirpos->data.file.reader_cls,
422                                                     0,
423                                                     dirpos->data.file.file_size,
424                                                     raw_data,
425                                                     &emsg))
426                         {
427                           GNUNET_free_non_null (emsg);
428                           GNUNET_free (raw_data);
429                           raw_data = NULL;
430                         } 
431                     }
432                 }
433               GNUNET_FS_directory_builder_add (db,
434                                                dirpos->chk_uri,
435                                                dirpos->meta,
436                                                raw_data);
437               GNUNET_free_non_null (raw_data);
438               dirpos = dirpos->next;
439             }
440           GNUNET_FS_directory_builder_finish (db,
441                                               &p->data.dir.dir_size,
442                                               &p->data.dir.dir_data);
443           size = p->data.dir.dir_size;
444         }
445       p->chk_tree_depth = compute_depth (size);
446       p->chk_tree = GNUNET_malloc (p->chk_tree_depth * 
447                                    sizeof (struct ContentHashKey) *
448                                    GNUNET_FS_CHK_PER_INODE);
449       p->current_depth = p->chk_tree_depth;
450     }
451   if (p->current_depth == p->chk_tree_depth)
452     {
453       if (p->is_directory)
454         {
455           pt_size = GNUNET_MIN(GNUNET_FS_DBLOCK_SIZE,
456                                p->data.dir.dir_size - p->publish_offset);
457           dd = p->data.dir.dir_data;
458           pt_block = &dd[p->publish_offset];
459         }
460       else
461         {
462           pt_size = GNUNET_MIN(GNUNET_FS_DBLOCK_SIZE,
463                                p->data.file.file_size - p->publish_offset);
464           emsg = NULL;
465           if (pt_size !=
466               p->data.file.reader (p->data.file.reader_cls,
467                                    p->publish_offset,
468                                    pt_size,
469                                    iob,
470                                    &emsg))
471             {
472               GNUNET_asprintf (&p->emsg, 
473                                _("Upload failed: %s"),
474                                emsg);
475               GNUNET_free (emsg);
476               GNUNET_FS_file_information_sync (p);
477               pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
478               make_publish_status (&pi, sc, p);
479               pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
480               pi.value.publish.specifics.error.message = p->emsg;
481               p->client_info
482                 = sc->h->upcb (sc->h->upcb_cls,
483                                &pi);
484               /* continue with main (to propagate error up) */
485               sc->upload_task 
486                 = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
487                                                 GNUNET_NO,
488                                                 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
489                                                 GNUNET_SCHEDULER_NO_TASK,
490                                                 GNUNET_TIME_UNIT_ZERO,
491                                                 &do_upload,
492                                                 sc);
493               return;
494             }
495           pt_block = iob;
496         }
497     }
498   else
499     {
500       pt_size = compute_iblock_size (p->chk_tree_depth - p->current_depth,
501                                      p->publish_offset); 
502       pt_block = &p->chk_tree[p->current_depth *
503                               GNUNET_FS_CHK_PER_INODE];
504     }
505   off = compute_chk_offset (p->chk_tree_depth - p->current_depth,
506                             p->publish_offset);
507   mychk = &p->chk_tree[(p->current_depth-1)*GNUNET_FS_CHK_PER_INODE+off];
508   GNUNET_CRYPTO_hash (pt_block, pt_size, &mychk->key);
509   GNUNET_CRYPTO_hash_to_aes_key (&mychk->key, &sk, &iv);
510   GNUNET_CRYPTO_aes_encrypt (pt_block,
511                              pt_size,
512                              &sk,
513                              &iv,
514                              enc);
515   // NOTE: this call (and progress below) is all that really differs
516   // between publish/unindex!  Parameterize & move this code!
517   // FIXME: something around here would need to change
518   // for indexing!
519   publish_block (sc, p, 
520                  &mychk->query,
521                  enc, 
522                  pt_size, 
523                  (p->current_depth == p->chk_tree_depth) 
524                  ? GNUNET_DATASTORE_BLOCKTYPE_DBLOCK 
525                  : GNUNET_DATASTORE_BLOCKTYPE_IBLOCK,
526                  &do_upload);
527   if (p->current_depth == p->chk_tree_depth)
528     {
529       pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
530       make_publish_status (&pi, sc, p);
531       pi.value.publish.specifics.progress.data = pt_block;
532       pi.value.publish.specifics.progress.offset = p->publish_offset;
533       pi.value.publish.specifics.progress.data_len = pt_size;
534       p->client_info 
535         = sc->h->upcb (sc->h->upcb_cls,
536                        &pi);
537     }
538   GNUNET_CRYPTO_hash (enc, pt_size, &mychk->query);
539   if (p->current_depth == p->chk_tree_depth) 
540     { 
541       p->publish_offset += pt_size;
542       if ( (p->publish_offset == size) ||
543            (0 == p->publish_offset % (GNUNET_FS_CHK_PER_INODE * GNUNET_FS_DBLOCK_SIZE) ) )
544         p->current_depth--;
545     }
546   else
547     {
548       if ( (off == GNUNET_FS_CHK_PER_INODE) ||
549            (p->publish_offset == size) )
550         p->current_depth--;
551       else
552         p->current_depth = p->chk_tree_depth;
553     }
554   if (0 == p->current_depth)
555     {
556       p->chk_uri = GNUNET_malloc (sizeof(struct GNUNET_FS_Uri));
557       p->chk_uri->type = chk;
558       p->chk_uri->data.chk.chk = p->chk_tree[0];
559       p->chk_uri->data.chk.file_length = size;
560       GNUNET_free (p->chk_tree);
561       p->chk_tree = NULL;
562     }
563 }
564
565
566 /**
567  * Main function that performs the upload.
568  * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
569  * @param tc task context
570  */
571 static void
572 do_upload (void *cls,
573            const struct GNUNET_SCHEDULER_TaskContext *tc)
574 {
575   struct GNUNET_FS_PublishContext *sc = cls;
576   struct GNUNET_FS_ProgressInfo pi;
577   struct GNUNET_FS_FileInformation *p;
578   char *fn;
579
580   sc->upload_task = GNUNET_SCHEDULER_NO_TASK;  
581   p = sc->fi_pos;
582   if (NULL == p)
583     {
584       /* upload of entire hierarchy complete,
585          publish namespace entries */
586       publish_sblock (sc);
587       return;
588     }
589   if (NULL != p->emsg)
590     {
591       /* error with current file, abort all
592          related files as well! */
593       while (NULL != p->dir)
594         {
595           fn = GNUNET_CONTAINER_meta_data_get_by_type (p->meta,
596                                                        EXTRACTOR_FILENAME);
597           p = p->dir;
598           GNUNET_asprintf (&p->emsg, 
599                            _("Recursive upload failed at `%s'"),
600                            fn);
601           GNUNET_free (fn);
602           GNUNET_FS_file_information_sync (p);
603           pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
604           make_publish_status (&pi, sc, p);
605           pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
606           pi.value.publish.specifics.error.message = p->emsg;
607           p->client_info
608             = sc->h->upcb (sc->h->upcb_cls,
609                            &pi);
610         }
611       return;
612     }
613   if (NULL != p->chk_uri)
614     {
615       /* move on to next file */
616       if (NULL != p->next)
617         sc->fi_pos = p->next;
618       else
619         sc->fi_pos = p->dir;
620       /* upload of "p" complete, publish KBlocks! */
621       publish_kblocks (sc, p);
622       return;
623     }
624   if ( (!p->is_directory) &&
625        (p->data.file.do_index) )
626     {
627       // FIXME: need to pre-compute hash over
628       // the entire file and ask FS to prepare
629       // for indexing!
630       return;
631     }
632   publish_content (sc, p);
633 }
634
635
636 /**
637  * Signal the FS's progress function that we are starting
638  * an upload.
639  *
640  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
641  * @param fi the entry in the publish-structure
642  * @param length length of the file or directory
643  * @param meta metadata for the file or directory (can be modified)
644  * @param uri pointer to the keywords that will be used for this entry (can be modified)
645  * @param anonymity pointer to selected anonymity level (can be modified)
646  * @param priority pointer to selected priority (can be modified)
647  * @param expirationTime pointer to selected expiration time (can be modified)
648  * @param client_info pointer to client context set upon creation (can be modified)
649  * @return GNUNET_OK to continue (always)
650  */
651 static int
652 fip_signal_start(void *cls,
653                  struct GNUNET_FS_FileInformation *fi,
654                  uint64_t length,
655                  struct GNUNET_CONTAINER_MetaData *meta,
656                  struct GNUNET_FS_Uri **uri,
657                  unsigned int *anonymity,
658                  unsigned int *priority,
659                  struct GNUNET_TIME_Absolute *expirationTime,
660                  void **client_info)
661 {
662   struct GNUNET_FS_PublishContext *sc = cls;
663   struct GNUNET_FS_ProgressInfo pi;
664
665   pi.status = GNUNET_FS_STATUS_PUBLISH_START;
666   make_publish_status (&pi, sc, fi);
667   *client_info = sc->h->upcb (sc->h->upcb_cls,
668                               &pi);
669   return GNUNET_OK;
670 }
671
672
673 /**
674  * Publish a file or directory.
675  *
676  * @param h handle to the file sharing subsystem
677  * @param ctx initial value to use for the '*ctx'
678  *        in the callback (for the GNUNET_FS_STATUS_PUBLISH_START event).
679  * @param fi information about the file or directory structure to publish
680  * @param namespace namespace to publish the file in, NULL for no namespace
681  * @param nid identifier to use for the publishd content in the namespace
682  *        (can be NULL, must be NULL if namespace is NULL)
683  * @param nuid update-identifier that will be used for future updates 
684  *        (can be NULL, must be NULL if namespace or nid is NULL)
685  * @return context that can be used to control the publish operation
686  */
687 struct GNUNET_FS_PublishContext *
688 GNUNET_FS_publish_start (struct GNUNET_FS_Handle *h,
689                          void *ctx,
690                          struct GNUNET_FS_FileInformation *fi,
691                          struct GNUNET_FS_Namespace *namespace,
692                          const char *nid,
693                          const char *nuid)
694 {
695   struct GNUNET_FS_PublishContext *ret;
696   struct GNUNET_FS_FileInformation *p;
697   struct GNUNET_DATASTORE_Handle *dsh;
698
699   dsh = GNUNET_DATASTORE_connect (h->cfg,
700                                   h->sched);
701   if (NULL == dsh)
702     return NULL;
703   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_PublishContext));
704   ret->dsh = dsh;
705   ret->h = h;
706   ret->client_ctx = ctx;
707   ret->fi = fi;
708   ret->namespace = namespace;
709   if (namespace != NULL)
710     {
711       namespace->rc++;
712       GNUNET_assert (NULL != nid);
713       ret->nid = GNUNET_strdup (nid);
714       if (NULL != nuid)
715         ret->nuid = GNUNET_strdup (nuid);
716     }
717   // FIXME: make upload persistent!
718
719   /* signal start */
720   GNUNET_FS_file_information_inspect (ret->fi,
721                                       &fip_signal_start,
722                                       ret);
723   /* find first leaf, DFS */
724   p = ret->fi;
725   while ( (p->is_directory) &&
726           (NULL != p->data.dir.entries) )
727     p = p->data.dir.entries;          
728   ret->fi_pos = p;
729
730   // FIXME: calculate space needed for "fi"
731   // and reserve as first task (then trigger
732   // "do_upload" from that continuation)!
733   ret->upload_task 
734     = GNUNET_SCHEDULER_add_delayed (h->sched,
735                                     GNUNET_NO,
736                                     GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
737                                     GNUNET_SCHEDULER_NO_TASK,
738                                     GNUNET_TIME_UNIT_ZERO,
739                                     &do_upload,
740                                     ret);
741   return ret;
742 }
743
744
745 /**
746  * Signal the FS's progress function that we are stopping
747  * an upload.
748  *
749  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
750  * @param fi the entry in the publish-structure
751  * @param length length of the file or directory
752  * @param meta metadata for the file or directory (can be modified)
753  * @param uri pointer to the keywords that will be used for this entry (can be modified)
754  * @param anonymity pointer to selected anonymity level (can be modified)
755  * @param priority pointer to selected priority (can be modified)
756  * @param expirationTime pointer to selected expiration time (can be modified)
757  * @param client_info pointer to client context set upon creation (can be modified)
758  * @return GNUNET_OK to continue (always)
759  */
760 static int
761 fip_signal_stop(void *cls,
762                 struct GNUNET_FS_FileInformation *fi,
763                 uint64_t length,
764                 struct GNUNET_CONTAINER_MetaData *meta,
765                 struct GNUNET_FS_Uri **uri,
766                 unsigned int *anonymity,
767                 unsigned int *priority,
768                 struct GNUNET_TIME_Absolute *expirationTime,
769                 void **client_info)
770 {
771   struct GNUNET_FS_PublishContext*sc = cls;
772   struct GNUNET_FS_ProgressInfo pi;
773
774   pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
775   make_publish_status (&pi, sc, fi);
776   GNUNET_break (NULL ==
777                 sc->h->upcb (sc->h->upcb_cls,
778                              &pi));
779   *client_info = NULL;
780   return GNUNET_OK;
781 }
782
783
784 /**
785  * Stop an upload.  Will abort incomplete uploads (but 
786  * not remove blocks that have already been publishd) or
787  * simply clean up the state for completed uploads.
788  *
789  * @param sc context for the upload to stop
790  */
791 void 
792 GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *sc)
793 {
794   if (GNUNET_SCHEDULER_NO_TASK != sc->upload_task)
795     GNUNET_SCHEDULER_cancel (sc->h->sched, sc->upload_task);
796   // FIXME: remove from persistence DB (?) --- think more about
797   //        shutdown / persistent-resume APIs!!!
798   GNUNET_FS_file_information_inspect (sc->fi,
799                                       &fip_signal_stop,
800                                       sc);
801   GNUNET_FS_file_information_destroy (sc->fi, NULL, NULL);
802   GNUNET_FS_namespace_delete (sc->namespace, GNUNET_NO);
803   GNUNET_free_non_null (sc->nid);  
804   GNUNET_free_non_null (sc->nuid);
805   GNUNET_DATASTORE_disconnect (sc->dsh, GNUNET_NO);
806   GNUNET_free (sc);
807 }
808
809 /* end of fs_publish.c */