2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
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.
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.
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.
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
31 * - code-sharing with unindex (can wait)
32 * - persistence support (can wait)
33 * - datastore reservation support (optimization)
37 #include "gnunet_constants.h"
38 #include "gnunet_signatures.h"
39 #include "gnunet_util_lib.h"
40 #include "gnunet_fs_service.h"
43 #define DEBUG_PUBLISH GNUNET_YES
45 #define MAX_KBLOCK_SIZE 60000
48 * Main function that performs the upload.
49 * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
50 * @param tc task context
54 const struct GNUNET_SCHEDULER_TaskContext *tc);
58 * Context for "ds_put_cont".
63 * Current publishing context.
65 struct GNUNET_FS_PublishContext *sc;
68 * Specific file with the block.
70 struct GNUNET_FS_FileInformation *p;
73 * Function to run next, if any (can be NULL).
75 GNUNET_SCHEDULER_Task cont;
85 * Fill in all of the generic fields for
88 * @param pc structure to fill in
89 * @param sc overall publishing context
90 * @param p file information for the file being published
93 make_publish_status (struct GNUNET_FS_ProgressInfo *pi,
94 struct GNUNET_FS_PublishContext *sc,
95 const struct GNUNET_FS_FileInformation *p)
97 pi->value.publish.sc = sc;
98 pi->value.publish.fi = p;
99 pi->value.publish.cctx
101 pi->value.publish.pctx
102 = (NULL == p->dir) ? NULL : p->dir->client_info;
103 pi->value.publish.size
104 = (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
105 pi->value.publish.eta
106 = GNUNET_TIME_calculate_eta (p->start_time,
108 pi->value.publish.size);
109 pi->value.publish.duration = GNUNET_TIME_absolute_get_duration (p->start_time);
110 pi->value.publish.completed = p->publish_offset;
111 pi->value.publish.anonymity = p->anonymity;
116 * Cleanup the publish context, we're done
119 * @param pc struct to clean up after
122 publish_cleanup (struct GNUNET_FS_PublishContext *sc)
124 GNUNET_FS_file_information_destroy (sc->fi, NULL, NULL);
125 GNUNET_FS_namespace_delete (sc->namespace, GNUNET_NO);
126 GNUNET_free_non_null (sc->nid);
127 GNUNET_free_non_null (sc->nuid);
128 GNUNET_DATASTORE_disconnect (sc->dsh, GNUNET_NO);
134 * Function called by the datastore API with
135 * the result from the PUT request.
137 * @param cls our closure
138 * @param success GNUNET_OK on success
139 * @param msg error message (or NULL)
142 ds_put_cont (void *cls,
146 struct PutContCtx *pcc = cls;
147 struct GNUNET_FS_ProgressInfo pi;
149 if (GNUNET_SYSERR == pcc->sc->in_network_wait)
151 /* we were aborted in the meantime,
153 publish_cleanup (pcc->sc);
156 GNUNET_assert (GNUNET_YES == pcc->sc->in_network_wait);
157 pcc->sc->in_network_wait = GNUNET_NO;
158 if (GNUNET_OK != success)
160 GNUNET_asprintf (&pcc->p->emsg,
161 _("Upload failed: %s"),
163 GNUNET_FS_file_information_sync (pcc->p);
164 pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
165 make_publish_status (&pi, pcc->sc, pcc->p);
166 pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
167 pi.value.publish.specifics.error.message = pcc->p->emsg;
169 = pcc->sc->h->upcb (pcc->sc->h->upcb_cls,
173 GNUNET_FS_file_information_sync (pcc->p);
174 if (NULL != pcc->cont)
176 = GNUNET_SCHEDULER_add_delayed (pcc->sc->h->sched,
178 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
179 GNUNET_SCHEDULER_NO_TASK,
180 GNUNET_TIME_UNIT_ZERO,
188 * Generate the callback that signals clients
189 * that a file (or directory) has been completely
192 * @param p the completed upload
193 * @param sc context of the publication
196 signal_publish_completion (struct GNUNET_FS_FileInformation *p,
197 struct GNUNET_FS_PublishContext *sc)
199 struct GNUNET_FS_ProgressInfo pi;
201 pi.status = GNUNET_FS_STATUS_PUBLISH_COMPLETED;
202 make_publish_status (&pi, sc, p);
203 pi.value.publish.eta = GNUNET_TIME_UNIT_ZERO;
204 pi.value.publish.specifics.completed.chk_uri = p->chk_uri;
206 = sc->h->upcb (sc->h->upcb_cls,
212 * Generate the callback that signals clients
213 * that a file (or directory) has encountered
214 * a problem during publication.
216 * @param p the upload that had trouble
217 * @param sc context of the publication
218 * @param emsg error message
221 signal_publish_error (struct GNUNET_FS_FileInformation *p,
222 struct GNUNET_FS_PublishContext *sc,
225 struct GNUNET_FS_ProgressInfo pi;
227 p->emsg = GNUNET_strdup (emsg);
228 pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
229 make_publish_status (&pi, sc, p);
230 pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
231 pi.value.publish.specifics.error.message =emsg;
233 = sc->h->upcb (sc->h->upcb_cls,
239 * We've finished publishing the SBlock as part of a larger upload.
240 * Check the result and complete the larger upload.
242 * @param cls the "struct GNUNET_FS_PublishContext*" of the larger upload
243 * @param uri URI of the published SBlock
244 * @param emsg NULL on success, otherwise error message
247 publish_sblocks_cont (void *cls,
248 const struct GNUNET_FS_Uri *uri,
251 struct GNUNET_FS_PublishContext *sc = cls;
254 signal_publish_error (sc->fi,
259 // FIXME: release the datastore reserve here!
260 signal_publish_completion (sc->fi, sc);
265 * We are almost done publishing the structure,
266 * add SBlocks (if needed).
268 * @param sc overall upload data
271 publish_sblock (struct GNUNET_FS_PublishContext *sc)
273 if (NULL != sc->namespace)
274 GNUNET_FS_publish_sks (sc->h,
280 sc->fi->expirationTime,
284 &publish_sblocks_cont,
287 publish_sblocks_cont (sc, NULL, NULL);
292 * We've finished publishing a KBlock
293 * as part of a larger upload. Check
294 * the result and continue the larger
297 * @param cls the "struct GNUNET_FS_PublishContext*" of the larger upload
298 * @param uri URI of the published blocks
299 * @param emsg NULL on success, otherwise error message
302 publish_kblocks_cont (void *cls,
303 const struct GNUNET_FS_Uri *uri,
306 struct GNUNET_FS_PublishContext *sc = cls;
307 struct GNUNET_FS_FileInformation *p = sc->fi_pos;
311 signal_publish_error (p, sc, emsg);
313 = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
315 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
316 GNUNET_SCHEDULER_NO_TASK,
317 GNUNET_TIME_UNIT_ZERO,
322 GNUNET_FS_file_information_sync (p);
324 signal_publish_completion (p, sc);
325 /* move on to next file */
327 sc->fi_pos = p->next;
331 = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
333 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
334 GNUNET_SCHEDULER_NO_TASK,
335 GNUNET_TIME_UNIT_ZERO,
342 * Compute the depth of the CHK tree.
344 * @param flen file length for which to compute the depth
345 * @return depth of the tree
348 compute_depth (uint64_t flen)
350 unsigned int treeDepth;
354 fl = GNUNET_FS_DBLOCK_SIZE;
358 if (fl * GNUNET_FS_CHK_PER_INODE < fl)
360 /* integer overflow, this is a HUGE file... */
363 fl = fl * GNUNET_FS_CHK_PER_INODE;
370 * Compute the size of the current IBlock.
372 * @param height height of the IBlock in the tree (aka overall
373 * number of tree levels minus depth); 0 == DBlock
374 * @param offset current offset in the overall file
375 * @return size of the corresponding IBlock
378 compute_iblock_size (unsigned int height,
386 GNUNET_assert (height > 0);
387 bds = GNUNET_FS_DBLOCK_SIZE; /* number of bytes each CHK at level "i"
389 for (i=0;i<height;i++)
390 bds *= GNUNET_FS_CHK_PER_INODE;
394 /* we were triggered at the end of a full block */
395 ret = GNUNET_FS_CHK_PER_INODE;
399 /* we were triggered at the end of the file */
400 bds /= GNUNET_FS_CHK_PER_INODE;
405 return (uint16_t) (ret * sizeof(struct ContentHashKey));
410 * Compute the offset of the CHK for the
411 * current block in the IBlock above.
413 * @param height height of the IBlock in the tree (aka overall
414 * number of tree levels minus depth); 0 == DBlock
415 * @param offset current offset in the overall file
416 * @return (array of CHKs') offset in the above IBlock
419 compute_chk_offset (unsigned int height,
426 bds = GNUNET_FS_DBLOCK_SIZE; /* number of bytes each CHK at level "i"
428 for (i=0;i<height;i++)
429 bds *= GNUNET_FS_CHK_PER_INODE;
430 GNUNET_assert (0 == (offset % bds));
432 return ret % GNUNET_FS_CHK_PER_INODE;
437 * We are uploading a file or directory; load (if necessary) the next
438 * block into memory, encrypt it and send it to the FS service. Then
439 * continue with the main task.
441 * @param sc overall upload data
442 * @param p specific file or directory for which kblocks
446 publish_content (struct GNUNET_FS_PublishContext *sc,
447 struct GNUNET_FS_FileInformation *p)
449 struct GNUNET_FS_ProgressInfo pi;
450 struct ContentHashKey *mychk;
451 const void *pt_block;
454 char iob[GNUNET_FS_DBLOCK_SIZE];
455 char enc[GNUNET_FS_DBLOCK_SIZE];
456 struct GNUNET_CRYPTO_AesSessionKey sk;
457 struct GNUNET_CRYPTO_AesInitializationVector iv;
460 struct GNUNET_FS_DirectoryBuilder *db;
461 struct GNUNET_FS_FileInformation *dirpos;
464 struct PutContCtx * dpc_cls;
466 // FIXME: figure out how to share this code
468 size = (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
469 if (NULL == p->chk_tree)
473 db = GNUNET_FS_directory_builder_create (p->meta);
474 dirpos = p->data.dir.entries;
475 while (NULL != dirpos)
477 if (dirpos->is_directory)
479 raw_data = dirpos->data.dir.dir_data;
480 dirpos->data.dir.dir_data = NULL;
485 if ( (dirpos->data.file.file_size < GNUNET_FS_MAX_INLINE_SIZE) &&
486 (dirpos->data.file.file_size > 0) )
488 raw_data = GNUNET_malloc (dirpos->data.file.file_size);
490 if (dirpos->data.file.file_size !=
491 dirpos->data.file.reader (dirpos->data.file.reader_cls,
493 dirpos->data.file.file_size,
497 GNUNET_free_non_null (emsg);
498 GNUNET_free (raw_data);
503 GNUNET_FS_directory_builder_add (db,
507 GNUNET_free_non_null (raw_data);
508 dirpos = dirpos->next;
510 GNUNET_FS_directory_builder_finish (db,
511 &p->data.dir.dir_size,
512 &p->data.dir.dir_data);
513 size = p->data.dir.dir_size;
515 p->chk_tree_depth = compute_depth (size);
516 p->chk_tree = GNUNET_malloc (p->chk_tree_depth *
517 sizeof (struct ContentHashKey) *
518 GNUNET_FS_CHK_PER_INODE);
519 p->current_depth = p->chk_tree_depth;
521 if (p->current_depth == p->chk_tree_depth)
525 pt_size = GNUNET_MIN(GNUNET_FS_DBLOCK_SIZE,
526 p->data.dir.dir_size - p->publish_offset);
527 dd = p->data.dir.dir_data;
528 pt_block = &dd[p->publish_offset];
532 pt_size = GNUNET_MIN(GNUNET_FS_DBLOCK_SIZE,
533 p->data.file.file_size - p->publish_offset);
536 p->data.file.reader (p->data.file.reader_cls,
542 GNUNET_asprintf (&p->emsg,
543 _("Upload failed: %s"),
546 GNUNET_FS_file_information_sync (p);
547 pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
548 make_publish_status (&pi, sc, p);
549 pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
550 pi.value.publish.specifics.error.message = p->emsg;
552 = sc->h->upcb (sc->h->upcb_cls,
554 /* continue with main (to propagate error up) */
556 = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
558 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
559 GNUNET_SCHEDULER_NO_TASK,
560 GNUNET_TIME_UNIT_ZERO,
570 pt_size = compute_iblock_size (p->chk_tree_depth - p->current_depth,
572 pt_block = &p->chk_tree[p->current_depth *
573 GNUNET_FS_CHK_PER_INODE];
575 off = compute_chk_offset (p->chk_tree_depth - p->current_depth,
577 mychk = &p->chk_tree[(p->current_depth-1)*GNUNET_FS_CHK_PER_INODE+off];
578 GNUNET_CRYPTO_hash (pt_block, pt_size, &mychk->key);
579 GNUNET_CRYPTO_hash_to_aes_key (&mychk->key, &sk, &iv);
580 GNUNET_CRYPTO_aes_encrypt (pt_block,
585 // NOTE: this block below is all that really differs
586 // between publish/unindex! Parameterize & move this code!
587 // FIXME: something around here would need to change
592 = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
594 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
595 GNUNET_SCHEDULER_NO_TASK,
596 GNUNET_TIME_UNIT_ZERO,
602 GNUNET_assert (GNUNET_NO == sc->in_network_wait);
603 sc->in_network_wait = GNUNET_YES;
604 dpc_cls = GNUNET_malloc(sizeof(struct PutContCtx));
605 dpc_cls->cont = &do_upload;
606 dpc_cls->cont_cls = sc;
608 GNUNET_DATASTORE_put (sc->dsh,
613 (p->current_depth == p->chk_tree_depth)
614 ? GNUNET_DATASTORE_BLOCKTYPE_DBLOCK
615 : GNUNET_DATASTORE_BLOCKTYPE_IBLOCK,
619 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
623 if (p->current_depth == p->chk_tree_depth)
625 pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
626 make_publish_status (&pi, sc, p);
627 pi.value.publish.specifics.progress.data = pt_block;
628 pi.value.publish.specifics.progress.offset = p->publish_offset;
629 pi.value.publish.specifics.progress.data_len = pt_size;
631 = sc->h->upcb (sc->h->upcb_cls,
634 GNUNET_CRYPTO_hash (enc, pt_size, &mychk->query);
635 if (p->current_depth == p->chk_tree_depth)
637 p->publish_offset += pt_size;
638 if ( (p->publish_offset == size) ||
639 (0 == p->publish_offset % (GNUNET_FS_CHK_PER_INODE * GNUNET_FS_DBLOCK_SIZE) ) )
644 if ( (off == GNUNET_FS_CHK_PER_INODE) ||
645 (p->publish_offset == size) )
648 p->current_depth = p->chk_tree_depth;
650 if (0 == p->current_depth)
652 p->chk_uri = GNUNET_malloc (sizeof(struct GNUNET_FS_Uri));
653 p->chk_uri->type = chk;
654 p->chk_uri->data.chk.chk = p->chk_tree[0];
655 p->chk_uri->data.chk.file_length = size;
656 GNUNET_free (p->chk_tree);
663 * Main function that performs the upload.
664 * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
665 * @param tc task context
668 do_upload (void *cls,
669 const struct GNUNET_SCHEDULER_TaskContext *tc)
671 struct GNUNET_FS_PublishContext *sc = cls;
672 struct GNUNET_FS_ProgressInfo pi;
673 struct GNUNET_FS_FileInformation *p;
676 sc->upload_task = GNUNET_SCHEDULER_NO_TASK;
680 /* upload of entire hierarchy complete,
681 publish namespace entries */
685 /* find starting position */
686 while ( (p->is_directory) &&
687 (NULL != p->data.dir.entries) &&
689 (NULL == p->data.dir.entries->chk_uri) )
691 p = p->data.dir.entries;
697 /* error with current file, abort all
698 related files as well! */
699 while (NULL != p->dir)
701 fn = GNUNET_CONTAINER_meta_data_get_by_type (p->meta,
704 GNUNET_asprintf (&p->emsg,
705 _("Recursive upload failed at `%s'"),
708 GNUNET_FS_file_information_sync (p);
709 pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
710 make_publish_status (&pi, sc, p);
711 pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
712 pi.value.publish.specifics.error.message = p->emsg;
714 = sc->h->upcb (sc->h->upcb_cls,
719 /* handle completion */
720 if (NULL != p->chk_uri)
722 /* upload of "p" complete, publish KBlocks! */
723 GNUNET_FS_publish_ksk (sc->h,
731 &publish_kblocks_cont,
735 if ( (!p->is_directory) &&
736 (p->data.file.do_index) )
738 // FIXME: need to pre-compute hash over
739 // the entire file and ask FS to prepare
743 publish_content (sc, p);
748 * Signal the FS's progress function that we are starting
751 * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
752 * @param fi the entry in the publish-structure
753 * @param length length of the file or directory
754 * @param meta metadata for the file or directory (can be modified)
755 * @param uri pointer to the keywords that will be used for this entry (can be modified)
756 * @param anonymity pointer to selected anonymity level (can be modified)
757 * @param priority pointer to selected priority (can be modified)
758 * @param expirationTime pointer to selected expiration time (can be modified)
759 * @param client_info pointer to client context set upon creation (can be modified)
760 * @return GNUNET_OK to continue (always)
763 fip_signal_start(void *cls,
764 struct GNUNET_FS_FileInformation *fi,
766 struct GNUNET_CONTAINER_MetaData *meta,
767 struct GNUNET_FS_Uri **uri,
768 unsigned int *anonymity,
769 unsigned int *priority,
770 struct GNUNET_TIME_Absolute *expirationTime,
773 struct GNUNET_FS_PublishContext *sc = cls;
774 struct GNUNET_FS_ProgressInfo pi;
776 pi.status = GNUNET_FS_STATUS_PUBLISH_START;
777 make_publish_status (&pi, sc, fi);
778 *client_info = sc->h->upcb (sc->h->upcb_cls,
785 * Publish a file or directory.
787 * @param h handle to the file sharing subsystem
788 * @param ctx initial value to use for the '*ctx'
789 * in the callback (for the GNUNET_FS_STATUS_PUBLISH_START event).
790 * @param fi information about the file or directory structure to publish
791 * @param namespace namespace to publish the file in, NULL for no namespace
792 * @param nid identifier to use for the publishd content in the namespace
793 * (can be NULL, must be NULL if namespace is NULL)
794 * @param nuid update-identifier that will be used for future updates
795 * (can be NULL, must be NULL if namespace or nid is NULL)
796 * @param options options for the publication
797 * @return context that can be used to control the publish operation
799 struct GNUNET_FS_PublishContext *
800 GNUNET_FS_publish_start (struct GNUNET_FS_Handle *h,
802 struct GNUNET_FS_FileInformation *fi,
803 struct GNUNET_FS_Namespace *namespace,
806 enum GNUNET_FS_PublishOptions options)
808 struct GNUNET_FS_PublishContext *ret;
809 struct GNUNET_DATASTORE_Handle *dsh;
811 if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
813 dsh = GNUNET_DATASTORE_connect (h->cfg,
822 ret = GNUNET_malloc (sizeof (struct GNUNET_FS_PublishContext));
825 ret->client_ctx = ctx;
827 ret->namespace = namespace;
828 if (namespace != NULL)
831 GNUNET_assert (NULL != nid);
832 ret->nid = GNUNET_strdup (nid);
834 ret->nuid = GNUNET_strdup (nuid);
836 // FIXME: make upload persistent!
839 GNUNET_FS_file_information_inspect (ret->fi,
842 ret->fi_pos = ret->fi;
844 // FIXME: calculate space needed for "fi"
845 // and reserve as first task (then trigger
846 // "do_upload" from that continuation)!
848 = GNUNET_SCHEDULER_add_delayed (h->sched,
850 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
851 GNUNET_SCHEDULER_NO_TASK,
852 GNUNET_TIME_UNIT_ZERO,
860 * Signal the FS's progress function that we are stopping
863 * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
864 * @param fi the entry in the publish-structure
865 * @param length length of the file or directory
866 * @param meta metadata for the file or directory (can be modified)
867 * @param uri pointer to the keywords that will be used for this entry (can be modified)
868 * @param anonymity pointer to selected anonymity level (can be modified)
869 * @param priority pointer to selected priority (can be modified)
870 * @param expirationTime pointer to selected expiration time (can be modified)
871 * @param client_info pointer to client context set upon creation (can be modified)
872 * @return GNUNET_OK to continue (always)
875 fip_signal_stop(void *cls,
876 struct GNUNET_FS_FileInformation *fi,
878 struct GNUNET_CONTAINER_MetaData *meta,
879 struct GNUNET_FS_Uri **uri,
880 unsigned int *anonymity,
881 unsigned int *priority,
882 struct GNUNET_TIME_Absolute *expirationTime,
885 struct GNUNET_FS_PublishContext*sc = cls;
886 struct GNUNET_FS_ProgressInfo pi;
888 pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
889 make_publish_status (&pi, sc, fi);
890 GNUNET_break (NULL ==
891 sc->h->upcb (sc->h->upcb_cls,
899 * Stop an upload. Will abort incomplete uploads (but
900 * not remove blocks that have already been publishd) or
901 * simply clean up the state for completed uploads.
903 * @param sc context for the upload to stop
906 GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *sc)
908 if (GNUNET_SCHEDULER_NO_TASK != sc->upload_task)
909 GNUNET_SCHEDULER_cancel (sc->h->sched, sc->upload_task);
910 // FIXME: remove from persistence DB (?) --- think more about
911 // shutdown / persistent-resume APIs!!!
912 GNUNET_FS_file_information_inspect (sc->fi,
915 if (GNUNET_YES == sc->in_network_wait)
917 sc->in_network_wait = GNUNET_SYSERR;
920 publish_cleanup (sc);
925 * Context for the KSK publication.
927 struct PublishKskContext
933 struct GNUNET_FS_Uri *ksk_uri;
938 struct GNUNET_FS_Handle *h;
941 * The master block that we are sending
942 * (in plaintext), has "mdsize+slen" more
943 * bytes than the struct would suggest.
945 struct GNUNET_FS_KBlock *kb;
948 * Buffer of the same size as "kb" for
949 * the encrypted version.
951 struct GNUNET_FS_KBlock *cpy;
954 * Handle to the datastore, NULL if we are just
957 struct GNUNET_DATASTORE_Handle *dsh;
960 * Function to call once we're done.
962 GNUNET_FS_PublishContinuation cont;
970 * When should the KBlocks expire?
972 struct GNUNET_TIME_Absolute expirationTime;
975 * Size of the serialized metadata.
980 * Size of the (CHK) URI as a string.
985 * Keyword that we are currently processing.
990 * Anonymity level for the KBlocks.
992 unsigned int anonymity;
995 * Priority for the KBlocks.
997 unsigned int priority;
1002 * Continuation of "GNUNET_FS_publish_ksk" that performs
1003 * the actual publishing operation (iterating over all
1006 * @param cls closure of type "struct PublishKskContext*"
1010 publish_ksk_cont (void *cls,
1011 const struct GNUNET_SCHEDULER_TaskContext *tc);
1015 * Function called by the datastore API with
1016 * the result from the PUT request.
1018 * @param cls closure of type "struct PublishKskContext*"
1019 * @param success GNUNET_OK on success
1020 * @param msg error message (or NULL)
1023 kb_put_cont (void *cls,
1027 struct PublishKskContext *pkc = cls;
1029 if (GNUNET_OK != success)
1031 GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1032 GNUNET_free (pkc->cpy);
1033 GNUNET_free (pkc->kb);
1034 pkc->cont (pkc->cont_cls,
1037 GNUNET_FS_uri_destroy (pkc->ksk_uri);
1041 GNUNET_SCHEDULER_add_continuation (pkc->h->sched,
1045 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1050 * Continuation of "GNUNET_FS_publish_ksk" that performs
1051 * the actual publishing operation (iterating over all
1054 * @param cls closure of type "struct PublishKskContext*"
1058 publish_ksk_cont (void *cls,
1059 const struct GNUNET_SCHEDULER_TaskContext *tc)
1061 struct PublishKskContext *pkc = cls;
1062 const char *keyword;
1063 GNUNET_HashCode key;
1064 GNUNET_HashCode query;
1065 struct GNUNET_CRYPTO_AesSessionKey skey;
1066 struct GNUNET_CRYPTO_AesInitializationVector iv;
1067 struct GNUNET_CRYPTO_RsaPrivateKey *pk;
1070 if ( (pkc->i == pkc->ksk_uri->data.ksk.keywordCount) ||
1071 (NULL == pkc->dsh) )
1073 if (NULL != pkc->dsh)
1074 GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1075 GNUNET_free (pkc->cpy);
1076 GNUNET_free (pkc->kb);
1077 pkc->cont (pkc->cont_cls,
1080 GNUNET_FS_uri_destroy (pkc->ksk_uri);
1084 keyword = pkc->ksk_uri->data.ksk.keywords[pkc->i++];
1085 /* first character of keyword indicates if it is
1086 mandatory or not -- ignore for hashing */
1087 GNUNET_CRYPTO_hash (&keyword[1], strlen (&keyword[1]), &key);
1088 GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv);
1089 GNUNET_CRYPTO_aes_encrypt (&pkc->kb[1],
1090 pkc->slen + pkc->mdsize,
1094 pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&key);
1095 GNUNET_CRYPTO_rsa_key_get_public (pk, &pkc->cpy->keyspace);
1096 GNUNET_CRYPTO_hash (&pkc->cpy->keyspace,
1097 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1099 GNUNET_assert (GNUNET_OK ==
1100 GNUNET_CRYPTO_rsa_sign (pk,
1102 &pkc->cpy->signature));
1103 GNUNET_CRYPTO_rsa_key_free (pk);
1104 GNUNET_DATASTORE_put (pkc->dsh,
1108 sizeof (struct GNUNET_FS_KBlock) +
1111 GNUNET_DATASTORE_BLOCKTYPE_KBLOCK,
1114 pkc->expirationTime,
1115 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1122 * Publish a CHK under various keywords on GNUnet.
1124 * @param h handle to the file sharing subsystem
1125 * @param ksk_uri keywords to use
1126 * @param meta metadata to use
1127 * @param uri URI to refer to in the KBlock
1128 * @param expirationTime when the KBlock expires
1129 * @param anonymity anonymity level for the KBlock
1130 * @param priority priority for the KBlock
1131 * @param options publication options
1132 * @param cont continuation
1133 * @param cont_cls closure for cont
1136 GNUNET_FS_publish_ksk (struct GNUNET_FS_Handle *h,
1137 struct GNUNET_FS_Uri *ksk_uri,
1138 struct GNUNET_CONTAINER_MetaData *meta,
1139 struct GNUNET_FS_Uri *uri,
1140 struct GNUNET_TIME_Absolute expirationTime,
1141 unsigned int anonymity,
1142 unsigned int priority,
1143 enum GNUNET_FS_PublishOptions options,
1144 GNUNET_FS_PublishContinuation cont,
1147 struct PublishKskContext *pkc;
1152 pkc = GNUNET_malloc (sizeof (struct PublishKskContext));
1154 pkc->expirationTime = expirationTime;
1155 pkc->anonymity = anonymity;
1156 pkc->priority = priority;
1158 pkc->cont_cls = cont_cls;
1159 if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1161 pkc->dsh = GNUNET_DATASTORE_connect (h->cfg,
1163 if (pkc->dsh == NULL)
1165 cont (cont_cls, NULL, _("Could not connect to datastore."));
1170 pkc->mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (meta,
1171 GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
1172 GNUNET_assert (pkc->mdsize >= 0);
1173 uris = GNUNET_FS_uri_to_string (uri);
1174 pkc->slen = strlen (uris) + 1;
1175 size = pkc->mdsize + sizeof (struct GNUNET_FS_KBlock) + pkc->slen;
1176 if (size > MAX_KBLOCK_SIZE)
1178 size = MAX_KBLOCK_SIZE;
1179 pkc->mdsize = size - sizeof (struct GNUNET_FS_KBlock) - pkc->slen;
1181 pkc->kb = GNUNET_malloc (size);
1182 kbe = (char *) &pkc->kb[1];
1183 memcpy (kbe, uris, pkc->slen);
1185 pkc->mdsize = GNUNET_CONTAINER_meta_data_serialize (meta,
1188 GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
1189 if (pkc->mdsize == -1)
1193 GNUNET_free (pkc->kb);
1194 if (pkc->dsh != NULL)
1195 GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1196 cont (cont_cls, NULL, _("Internal error."));
1200 size = sizeof (struct GNUNET_FS_KBlock) + pkc->slen + pkc->mdsize;
1202 pkc->cpy = GNUNET_malloc (size);
1203 pkc->cpy->purpose.size = htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) + pkc->mdsize + pkc->slen);
1204 pkc->cpy->purpose.purpose = htonl(GNUNET_SIGNATURE_PURPOSE_FS_KBLOCK);
1205 pkc->ksk_uri = GNUNET_FS_uri_dup (ksk_uri);
1206 GNUNET_SCHEDULER_add_continuation (h->sched,
1210 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1215 * Publish an SBlock on GNUnet.
1217 * @param h handle to the file sharing subsystem
1218 * @param namespace namespace to publish in
1219 * @param identifier identifier to use
1220 * @param update update identifier to use
1221 * @param meta metadata to use
1222 * @param uri URI to refer to in the SBlock
1223 * @param expirationTime when the SBlock expires
1224 * @param anonymity anonymity level for the SBlock
1225 * @param priority priority for the SBlock
1226 * @param options publication options
1227 * @param cont continuation
1228 * @param cont_cls closure for cont
1231 GNUNET_FS_publish_sks (struct GNUNET_FS_Handle *h,
1232 struct GNUNET_FS_Namespace *namespace,
1233 const char *identifier,
1235 struct GNUNET_CONTAINER_MetaData *meta,
1236 struct GNUNET_FS_Uri *uri,
1237 struct GNUNET_TIME_Absolute expirationTime,
1238 unsigned int anonymity,
1239 unsigned int priority,
1240 enum GNUNET_FS_PublishOptions options,
1241 GNUNET_FS_PublishContinuation cont,
1245 struct GNUNET_ECRS_URI *uri;
1246 struct GNUNET_ClientServerConnection *sock;
1247 GNUNET_DatastoreValue *value;
1249 unsigned int mdsize;
1250 struct GNUNET_RSA_PrivateKey *hk;
1251 GNUNET_EC_SBlock *sb;
1254 GNUNET_HashCode hc; /* hash of thisId = key */
1255 GNUNET_HashCode hc2; /* hash of hc = identifier */
1257 unsigned int nidlen;
1259 hk = read_namespace_key (cfg, pid);
1263 /* THEN: construct GNUNET_EC_SBlock */
1264 dstURI = GNUNET_ECRS_uri_to_string (dstU);
1265 mdsize = GNUNET_meta_data_get_serialized_size (md, GNUNET_SERIALIZE_PART);
1268 nidlen = strlen (nextId) + 1;
1269 size = mdsize + sizeof (GNUNET_EC_SBlock) + strlen (dstURI) + 1 + nidlen;
1270 if (size > MAX_SBLOCK_SIZE)
1272 size = MAX_SBLOCK_SIZE;
1274 size - (sizeof (GNUNET_EC_SBlock) + strlen (dstURI) + 1 + nidlen);
1276 value = GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + size);
1277 sb = (GNUNET_EC_SBlock *) & value[1];
1278 sb->type = htonl (GNUNET_ECRS_BLOCKTYPE_SIGNED);
1279 destPos = (char *) &sb[1];
1280 memcpy (destPos, nextId, nidlen);
1282 memcpy (destPos, dstURI, strlen (dstURI) + 1);
1283 destPos += strlen (dstURI) + 1;
1284 mdsize = GNUNET_meta_data_serialize (ectx,
1287 mdsize, GNUNET_SERIALIZE_PART);
1290 GNUNET_GE_BREAK (ectx, 0);
1291 GNUNET_free (dstURI);
1292 GNUNET_RSA_free_key (hk);
1293 GNUNET_free (value);
1296 size = sizeof (GNUNET_EC_SBlock) + mdsize + strlen (dstURI) + 1 + nidlen;
1297 value->size = htonl (sizeof (GNUNET_DatastoreValue) + size);
1298 value->type = htonl (GNUNET_ECRS_BLOCKTYPE_SIGNED);
1299 value->priority = htonl (priority);
1300 value->anonymity_level = htonl (anonymityLevel);
1301 value->expiration_time = GNUNET_htonll (expiration);
1302 GNUNET_hash (thisId, strlen (thisId), &hc);
1303 GNUNET_hash (&hc, sizeof (GNUNET_HashCode), &hc2);
1304 uri = GNUNET_malloc (sizeof (URI));
1306 GNUNET_RSA_get_public_key (hk, &sb->subspace);
1307 GNUNET_hash (&sb->subspace,
1308 sizeof (GNUNET_RSA_PublicKey), &uri->data.sks.namespace);
1309 GNUNET_GE_BREAK (ectx, 0 == memcmp (&uri->data.sks.namespace,
1310 pid, sizeof (GNUNET_HashCode)));
1311 uri->data.sks.identifier = GNUNET_strdup (thisId);
1312 GNUNET_hash_xor (&hc2, &uri->data.sks.namespace, &sb->identifier);
1313 GNUNET_ECRS_encryptInPlace (&hc, &sb[1], size - sizeof (GNUNET_EC_SBlock));
1314 GNUNET_GE_ASSERT (ectx,
1315 GNUNET_OK == GNUNET_RSA_sign (hk,
1319 (GNUNET_RSA_Signature) -
1321 (GNUNET_RSA_PublicKey) -
1322 sizeof (unsigned int),
1325 GNUNET_RSA_free_key (hk);
1326 sock = GNUNET_client_connection_create (ectx, cfg);
1327 ret = GNUNET_FS_insert (sock, value);
1328 if (ret != GNUNET_OK)
1333 GNUNET_client_connection_destroy (sock);
1334 GNUNET_free (value);
1335 GNUNET_free (dstURI);
1339 /* end of fs_publish.c */