diff options
author | Christian Grothoff <christian@grothoff.org> | 2010-08-07 16:54:29 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2010-08-07 16:54:29 +0000 |
commit | d4115af6385dcab2433b27796e777819e297dae7 (patch) | |
tree | fee222cda66403a71e6827acf06306b9295a7d68 /src/fs/fs_namespace.c | |
parent | 63749e58a862ca90d958579040cbc3693e88db12 (diff) | |
download | gnunet-d4115af6385dcab2433b27796e777819e297dae7.tar.gz gnunet-d4115af6385dcab2433b27796e777819e297dae7.zip |
implement update api
Diffstat (limited to 'src/fs/fs_namespace.c')
-rw-r--r-- | src/fs/fs_namespace.c | 732 |
1 files changed, 728 insertions, 4 deletions
diff --git a/src/fs/fs_namespace.c b/src/fs/fs_namespace.c index ee3743733..a9bcaf8e5 100644 --- a/src/fs/fs_namespace.c +++ b/src/fs/fs_namespace.c | |||
@@ -59,6 +59,184 @@ get_namespace_directory (struct GNUNET_FS_Handle *h) | |||
59 | 59 | ||
60 | 60 | ||
61 | /** | 61 | /** |
62 | * Return the name of the directory in which we store | ||
63 | * the update information graph for the given local namespace. | ||
64 | * | ||
65 | * @param ns namespace handle | ||
66 | * @return NULL on error, otherwise the name of the directory | ||
67 | */ | ||
68 | static char * | ||
69 | get_update_information_directory (struct GNUNET_FS_Namespace *ns) | ||
70 | { | ||
71 | char *dn; | ||
72 | char *ret; | ||
73 | |||
74 | if (GNUNET_OK != | ||
75 | GNUNET_CONFIGURATION_get_value_filename (ns->h->cfg, | ||
76 | "FS", | ||
77 | "UPDATE_DIR", | ||
78 | &dn)) | ||
79 | { | ||
80 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
81 | _("Configuration fails to specify `%s' in section `%s'\n"), | ||
82 | "UPDATE_DIR", | ||
83 | "fs"); | ||
84 | return NULL; | ||
85 | } | ||
86 | GNUNET_asprintf (&ret, | ||
87 | "%s%s%s", | ||
88 | dn, | ||
89 | DIR_SEPARATOR_STR, | ||
90 | ns->name); | ||
91 | GNUNET_free (dn); | ||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | |||
96 | /** | ||
97 | * Write the namespace update node graph to a file. | ||
98 | * | ||
99 | * @param ns namespace to dump | ||
100 | */ | ||
101 | static void | ||
102 | write_update_information_graph (struct GNUNET_FS_Namespace *ns) | ||
103 | { | ||
104 | char * fn; | ||
105 | struct GNUNET_BIO_WriteHandle *wh; | ||
106 | unsigned int i; | ||
107 | struct NamespaceUpdateNode *n; | ||
108 | char *uris; | ||
109 | |||
110 | fn = get_update_information_directory (ns); | ||
111 | wh = GNUNET_BIO_write_open (fn); | ||
112 | if (wh == NULL) | ||
113 | { | ||
114 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
115 | _("Failed to open `%s' for writing: %s\n"), | ||
116 | STRERROR (errno)); | ||
117 | GNUNET_free (fn); | ||
118 | return; | ||
119 | } | ||
120 | if (GNUNET_OK != | ||
121 | GNUNET_BIO_write_int32 (wh, ns->update_node_count)) | ||
122 | goto END; | ||
123 | for (i=0;i<ns->update_node_count;i++) | ||
124 | { | ||
125 | n = ns->update_nodes[i]; | ||
126 | uris = GNUNET_FS_uri_to_string (n->uri); | ||
127 | if ( (GNUNET_OK != | ||
128 | GNUNET_BIO_write_string (wh, n->id)) || | ||
129 | (GNUNET_OK != | ||
130 | GNUNET_BIO_write_meta_data (wh, n->md)) || | ||
131 | (GNUNET_OK != | ||
132 | GNUNET_BIO_write_string (wh, n->update)) || | ||
133 | (GNUNET_OK != | ||
134 | GNUNET_BIO_write_string (wh, uris)) ) | ||
135 | { | ||
136 | GNUNET_free (uris); | ||
137 | break; | ||
138 | } | ||
139 | GNUNET_free (uris); | ||
140 | } | ||
141 | END: | ||
142 | if (GNUNET_OK != GNUNET_BIO_write_close (wh)) | ||
143 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
144 | _("Failed to write `%s': %s\n"), | ||
145 | STRERROR (errno)); | ||
146 | GNUNET_free (fn); | ||
147 | } | ||
148 | |||
149 | |||
150 | /** | ||
151 | * Read the namespace update node graph from a file. | ||
152 | * | ||
153 | * @param ns namespace to read | ||
154 | */ | ||
155 | static void | ||
156 | read_update_information_graph (struct GNUNET_FS_Namespace *ns) | ||
157 | { | ||
158 | char * fn; | ||
159 | struct GNUNET_BIO_ReadHandle *rh; | ||
160 | unsigned int i; | ||
161 | struct NamespaceUpdateNode *n; | ||
162 | char *uris; | ||
163 | uint32_t count; | ||
164 | char *emsg; | ||
165 | |||
166 | fn = get_update_information_directory (ns); | ||
167 | rh = GNUNET_BIO_read_open (fn); | ||
168 | if (rh == NULL) | ||
169 | { | ||
170 | GNUNET_free (fn); | ||
171 | return; | ||
172 | } | ||
173 | if (GNUNET_OK != | ||
174 | GNUNET_BIO_read_int32 (rh, &count)) | ||
175 | { | ||
176 | GNUNET_break (0); | ||
177 | goto END; | ||
178 | } | ||
179 | if (count > 1024 * 1024) | ||
180 | { | ||
181 | GNUNET_break (0); | ||
182 | goto END; | ||
183 | } | ||
184 | if (count == 0) | ||
185 | { | ||
186 | GNUNET_break (GNUNET_OK == GNUNET_BIO_read_close (rh, NULL)); | ||
187 | GNUNET_free (fn); | ||
188 | return; | ||
189 | } | ||
190 | ns->update_nodes = GNUNET_malloc (count * sizeof (struct NamespaceUpdateNode*)); | ||
191 | |||
192 | for (i=0;i<count;i++) | ||
193 | { | ||
194 | n = GNUNET_malloc (sizeof (struct NamespaceUpdateNode)); | ||
195 | if ( (GNUNET_OK != | ||
196 | GNUNET_BIO_read_string (rh, "identifier", &n->id, 1024)) || | ||
197 | (GNUNET_OK != | ||
198 | GNUNET_BIO_read_meta_data (rh, "meta", &n->md)) || | ||
199 | (GNUNET_OK != | ||
200 | GNUNET_BIO_read_string (rh, "update-id", &n->update, 1024)) || | ||
201 | (GNUNET_OK != | ||
202 | GNUNET_BIO_read_string (rh, "uri", &uris, 1024 * 2)) ) | ||
203 | { | ||
204 | GNUNET_break (0); | ||
205 | GNUNET_free_non_null (n->id); | ||
206 | GNUNET_free_non_null (n->update); | ||
207 | if (n->md != NULL) | ||
208 | GNUNET_CONTAINER_meta_data_destroy (n->md); | ||
209 | GNUNET_free (n); | ||
210 | break; | ||
211 | } | ||
212 | n->uri = GNUNET_FS_uri_parse (uris, &emsg); | ||
213 | GNUNET_free (uris); | ||
214 | if (n->uri == NULL) | ||
215 | { | ||
216 | GNUNET_break (0); | ||
217 | GNUNET_free (emsg); | ||
218 | GNUNET_free (n->id); | ||
219 | GNUNET_free (n->update); | ||
220 | GNUNET_CONTAINER_meta_data_destroy (n->md); | ||
221 | GNUNET_free (n); | ||
222 | break; | ||
223 | } | ||
224 | ns->update_nodes[i] = n; | ||
225 | } | ||
226 | ns->update_node_count = i; | ||
227 | END: | ||
228 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
229 | { | ||
230 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
231 | _("Failed to write `%s': %s\n"), | ||
232 | emsg); | ||
233 | GNUNET_free (emsg); | ||
234 | } | ||
235 | GNUNET_free (fn); | ||
236 | } | ||
237 | |||
238 | |||
239 | /** | ||
62 | * Context for advertising a namespace. | 240 | * Context for advertising a namespace. |
63 | */ | 241 | */ |
64 | struct AdvertisementContext | 242 | struct AdvertisementContext |
@@ -363,6 +541,7 @@ GNUNET_FS_namespace_create (struct GNUNET_FS_Handle *h, | |||
363 | name); | 541 | name); |
364 | GNUNET_free (dn); | 542 | GNUNET_free (dn); |
365 | ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Namespace)); | 543 | ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Namespace)); |
544 | ret->h = h; | ||
366 | ret->rc = 1; | 545 | ret->rc = 1; |
367 | ret->key = GNUNET_CRYPTO_rsa_key_create_from_file (fn); | 546 | ret->key = GNUNET_CRYPTO_rsa_key_create_from_file (fn); |
368 | if (ret->key == NULL) | 547 | if (ret->key == NULL) |
@@ -395,6 +574,9 @@ int | |||
395 | GNUNET_FS_namespace_delete (struct GNUNET_FS_Namespace *namespace, | 574 | GNUNET_FS_namespace_delete (struct GNUNET_FS_Namespace *namespace, |
396 | int freeze) | 575 | int freeze) |
397 | { | 576 | { |
577 | unsigned int i; | ||
578 | struct NamespaceUpdateNode *nsn; | ||
579 | |||
398 | namespace->rc--; | 580 | namespace->rc--; |
399 | if (freeze) | 581 | if (freeze) |
400 | { | 582 | { |
@@ -408,6 +590,20 @@ GNUNET_FS_namespace_delete (struct GNUNET_FS_Namespace *namespace, | |||
408 | GNUNET_CRYPTO_rsa_key_free (namespace->key); | 590 | GNUNET_CRYPTO_rsa_key_free (namespace->key); |
409 | GNUNET_free (namespace->filename); | 591 | GNUNET_free (namespace->filename); |
410 | GNUNET_free (namespace->name); | 592 | GNUNET_free (namespace->name); |
593 | for (i=0;i<namespace->update_node_count;i++) | ||
594 | { | ||
595 | nsn = namespace->update_nodes[i]; | ||
596 | GNUNET_CONTAINER_meta_data_destroy (nsn->md); | ||
597 | GNUNET_FS_uri_destroy (nsn->uri); | ||
598 | GNUNET_free (nsn->id); | ||
599 | GNUNET_free (nsn->update); | ||
600 | GNUNET_free (nsn); | ||
601 | } | ||
602 | GNUNET_array_grow (namespace->update_nodes, | ||
603 | namespace->update_node_count, | ||
604 | 0); | ||
605 | if (namespace->update_map != NULL) | ||
606 | GNUNET_CONTAINER_multihashmap_destroy (namespace->update_map); | ||
411 | GNUNET_free (namespace); | 607 | GNUNET_free (namespace); |
412 | } | 608 | } |
413 | return GNUNET_OK; | 609 | return GNUNET_OK; |
@@ -507,22 +703,550 @@ GNUNET_FS_namespace_list (struct GNUNET_FS_Handle *h, | |||
507 | 703 | ||
508 | 704 | ||
509 | 705 | ||
706 | |||
510 | /** | 707 | /** |
511 | * List all of the identifiers in the namespace for | 708 | * Context for the SKS publication. |
512 | * which we could produce an update. | 709 | */ |
710 | struct PublishSksContext | ||
711 | { | ||
712 | |||
713 | /** | ||
714 | * URI of the new entry in the namespace. | ||
715 | */ | ||
716 | struct GNUNET_FS_Uri *uri; | ||
717 | |||
718 | /** | ||
719 | * Namespace update node to add to namespace on success (or to be | ||
720 | * deleted if publishing failed). | ||
721 | */ | ||
722 | struct NamespaceUpdateNode *nsn; | ||
723 | |||
724 | /** | ||
725 | * Namespace we're publishing to. | ||
726 | */ | ||
727 | struct GNUNET_FS_Namespace *namespace; | ||
728 | |||
729 | /** | ||
730 | * Handle to the datastore. | ||
731 | */ | ||
732 | struct GNUNET_DATASTORE_Handle *dsh; | ||
733 | |||
734 | /** | ||
735 | * Function to call once we're done. | ||
736 | */ | ||
737 | GNUNET_FS_PublishContinuation cont; | ||
738 | |||
739 | /** | ||
740 | * Closure for cont. | ||
741 | */ | ||
742 | void *cont_cls; | ||
743 | |||
744 | }; | ||
745 | |||
746 | |||
747 | /** | ||
748 | * Function called by the datastore API with | ||
749 | * the result from the PUT (SBlock) request. | ||
750 | * | ||
751 | * @param cls closure of type "struct PublishSksContext*" | ||
752 | * @param success GNUNET_OK on success | ||
753 | * @param msg error message (or NULL) | ||
754 | */ | ||
755 | static void | ||
756 | sb_put_cont (void *cls, | ||
757 | int success, | ||
758 | const char *msg) | ||
759 | { | ||
760 | struct PublishSksContext *psc = cls; | ||
761 | GNUNET_HashCode hc; | ||
762 | |||
763 | if (NULL != psc->dsh) | ||
764 | { | ||
765 | GNUNET_DATASTORE_disconnect (psc->dsh, GNUNET_NO); | ||
766 | psc->dsh = NULL; | ||
767 | } | ||
768 | if (GNUNET_OK != success) | ||
769 | { | ||
770 | psc->cont (psc->cont_cls, | ||
771 | NULL, | ||
772 | msg); | ||
773 | } | ||
774 | else | ||
775 | { | ||
776 | if (psc->nsn != NULL) | ||
777 | { | ||
778 | /* FIXME: this can be done much more | ||
779 | efficiently by simply appending to the | ||
780 | file and overwriting the 4-byte header */ | ||
781 | if (psc->namespace->update_nodes == NULL) | ||
782 | read_update_information_graph (psc->namespace); | ||
783 | GNUNET_array_append (psc->namespace->update_nodes, | ||
784 | psc->namespace->update_node_count, | ||
785 | psc->nsn); | ||
786 | if (psc->namespace->update_map != NULL) | ||
787 | { | ||
788 | GNUNET_CRYPTO_hash (psc->nsn->id, | ||
789 | strlen (psc->nsn->id), | ||
790 | &hc); | ||
791 | GNUNET_CONTAINER_multihashmap_put (psc->namespace->update_map, | ||
792 | &hc, | ||
793 | psc->nsn, | ||
794 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
795 | } | ||
796 | psc->nsn = NULL; | ||
797 | write_update_information_graph (psc->namespace); | ||
798 | } | ||
799 | psc->cont (psc->cont_cls, | ||
800 | psc->uri, | ||
801 | NULL); | ||
802 | } | ||
803 | GNUNET_FS_namespace_delete (psc->namespace, | ||
804 | GNUNET_NO); | ||
805 | GNUNET_FS_uri_destroy (psc->uri); | ||
806 | if (psc->nsn != NULL) | ||
807 | { | ||
808 | GNUNET_CONTAINER_meta_data_destroy (psc->nsn->md); | ||
809 | GNUNET_FS_uri_destroy (psc->nsn->uri); | ||
810 | GNUNET_free (psc->nsn->id); | ||
811 | GNUNET_free (psc->nsn->update); | ||
812 | GNUNET_free (psc->nsn); | ||
813 | } | ||
814 | GNUNET_free (psc); | ||
815 | } | ||
816 | |||
817 | |||
818 | /** | ||
819 | * Publish an SBlock on GNUnet. | ||
820 | * | ||
821 | * @param h handle to the file sharing subsystem | ||
822 | * @param namespace namespace to publish in | ||
823 | * @param identifier identifier to use | ||
824 | * @param update update identifier to use | ||
825 | * @param meta metadata to use | ||
826 | * @param uri URI to refer to in the SBlock | ||
827 | * @param expirationTime when the SBlock expires | ||
828 | * @param anonymity anonymity level for the SBlock | ||
829 | * @param priority priority for the SBlock | ||
830 | * @param options publication options | ||
831 | * @param cont continuation | ||
832 | * @param cont_cls closure for cont | ||
833 | */ | ||
834 | void | ||
835 | GNUNET_FS_publish_sks (struct GNUNET_FS_Handle *h, | ||
836 | struct GNUNET_FS_Namespace *namespace, | ||
837 | const char *identifier, | ||
838 | const char *update, | ||
839 | const struct GNUNET_CONTAINER_MetaData *meta, | ||
840 | const struct GNUNET_FS_Uri *uri, | ||
841 | struct GNUNET_TIME_Absolute expirationTime, | ||
842 | uint32_t anonymity, | ||
843 | uint32_t priority, | ||
844 | enum GNUNET_FS_PublishOptions options, | ||
845 | GNUNET_FS_PublishContinuation cont, | ||
846 | void *cont_cls) | ||
847 | { | ||
848 | struct PublishSksContext *psc; | ||
849 | struct GNUNET_CRYPTO_AesSessionKey sk; | ||
850 | struct GNUNET_CRYPTO_AesInitializationVector iv; | ||
851 | struct GNUNET_FS_Uri *sks_uri; | ||
852 | char *uris; | ||
853 | size_t size; | ||
854 | size_t slen; | ||
855 | size_t nidlen; | ||
856 | size_t idlen; | ||
857 | ssize_t mdsize; | ||
858 | struct SBlock *sb; | ||
859 | struct SBlock *sb_enc; | ||
860 | char *dest; | ||
861 | struct GNUNET_CONTAINER_MetaData *mmeta; | ||
862 | GNUNET_HashCode key; /* hash of thisId = key */ | ||
863 | GNUNET_HashCode id; /* hash of hc = identifier */ | ||
864 | GNUNET_HashCode query; /* id ^ nsid = DB query */ | ||
865 | |||
866 | if (NULL == meta) | ||
867 | mmeta = GNUNET_CONTAINER_meta_data_create (); | ||
868 | else | ||
869 | mmeta = GNUNET_CONTAINER_meta_data_duplicate (meta); | ||
870 | uris = GNUNET_FS_uri_to_string (uri); | ||
871 | slen = strlen (uris) + 1; | ||
872 | idlen = strlen (identifier); | ||
873 | if (update == NULL) | ||
874 | update = ""; | ||
875 | nidlen = strlen (update) + 1; | ||
876 | mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (mmeta); | ||
877 | size = sizeof (struct SBlock) + slen + nidlen + mdsize; | ||
878 | if (size > MAX_SBLOCK_SIZE) | ||
879 | { | ||
880 | size = MAX_SBLOCK_SIZE; | ||
881 | mdsize = size - (sizeof (struct SBlock) + slen + nidlen); | ||
882 | } | ||
883 | sb = GNUNET_malloc (sizeof (struct SBlock) + size); | ||
884 | dest = (char *) &sb[1]; | ||
885 | memcpy (dest, update, nidlen); | ||
886 | dest += nidlen; | ||
887 | memcpy (dest, uris, slen); | ||
888 | GNUNET_free (uris); | ||
889 | dest += slen; | ||
890 | mdsize = GNUNET_CONTAINER_meta_data_serialize (mmeta, | ||
891 | &dest, | ||
892 | mdsize, | ||
893 | GNUNET_CONTAINER_META_DATA_SERIALIZE_PART); | ||
894 | GNUNET_CONTAINER_meta_data_destroy (mmeta); | ||
895 | if (mdsize == -1) | ||
896 | { | ||
897 | GNUNET_break (0); | ||
898 | GNUNET_free (sb); | ||
899 | cont (cont_cls, | ||
900 | NULL, | ||
901 | _("Internal error.")); | ||
902 | return; | ||
903 | } | ||
904 | size = sizeof (struct SBlock) + mdsize + slen + nidlen; | ||
905 | sb_enc = GNUNET_malloc (size); | ||
906 | GNUNET_CRYPTO_hash (identifier, idlen, &key); | ||
907 | GNUNET_CRYPTO_hash (&key, sizeof (GNUNET_HashCode), &id); | ||
908 | sks_uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri)); | ||
909 | sks_uri->type = sks; | ||
910 | GNUNET_CRYPTO_rsa_key_get_public (namespace->key, &sb_enc->subspace); | ||
911 | GNUNET_CRYPTO_hash (&sb_enc->subspace, | ||
912 | sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), | ||
913 | &sks_uri->data.sks.namespace); | ||
914 | sks_uri->data.sks.identifier = GNUNET_strdup (identifier); | ||
915 | GNUNET_CRYPTO_hash_xor (&id, | ||
916 | &sks_uri->data.sks.namespace, | ||
917 | &sb_enc->identifier); | ||
918 | GNUNET_CRYPTO_hash_to_aes_key (&key, &sk, &iv); | ||
919 | GNUNET_CRYPTO_aes_encrypt (&sb[1], | ||
920 | size - sizeof (struct SBlock), | ||
921 | &sk, | ||
922 | &iv, | ||
923 | &sb_enc[1]); | ||
924 | sb_enc->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_FS_SBLOCK); | ||
925 | sb_enc->purpose.size = htonl(slen + mdsize + nidlen | ||
926 | + sizeof(struct SBlock) | ||
927 | - sizeof(struct GNUNET_CRYPTO_RsaSignature)); | ||
928 | GNUNET_assert (GNUNET_OK == | ||
929 | GNUNET_CRYPTO_rsa_sign (namespace->key, | ||
930 | &sb_enc->purpose, | ||
931 | &sb_enc->signature)); | ||
932 | psc = GNUNET_malloc (sizeof(struct PublishSksContext)); | ||
933 | psc->uri = sks_uri; | ||
934 | psc->cont = cont; | ||
935 | psc->namespace = namespace; | ||
936 | namespace->rc++; | ||
937 | psc->cont_cls = cont_cls; | ||
938 | if (0 != (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY)) | ||
939 | { | ||
940 | GNUNET_free (sb_enc); | ||
941 | GNUNET_free (sb); | ||
942 | sb_put_cont (psc, | ||
943 | GNUNET_OK, | ||
944 | NULL); | ||
945 | return; | ||
946 | } | ||
947 | psc->dsh = GNUNET_DATASTORE_connect (h->cfg, h->sched); | ||
948 | if (NULL == psc->dsh) | ||
949 | { | ||
950 | GNUNET_free (sb_enc); | ||
951 | GNUNET_free (sb); | ||
952 | sb_put_cont (psc, | ||
953 | GNUNET_NO, | ||
954 | _("Failed to connect to datastore.")); | ||
955 | return; | ||
956 | } | ||
957 | GNUNET_CRYPTO_hash_xor (&sks_uri->data.sks.namespace, | ||
958 | &id, | ||
959 | &query); | ||
960 | if (NULL != update) | ||
961 | { | ||
962 | psc->nsn = GNUNET_malloc (sizeof (struct NamespaceUpdateNode)); | ||
963 | psc->nsn->id = GNUNET_strdup (identifier); | ||
964 | psc->nsn->update = GNUNET_strdup (update); | ||
965 | psc->nsn->md = GNUNET_CONTAINER_meta_data_duplicate (meta); | ||
966 | psc->nsn->uri = GNUNET_FS_uri_dup (uri); | ||
967 | } | ||
968 | GNUNET_DATASTORE_put (psc->dsh, | ||
969 | 0, | ||
970 | &sb_enc->identifier, | ||
971 | size, | ||
972 | sb_enc, | ||
973 | GNUNET_BLOCK_TYPE_SBLOCK, | ||
974 | priority, | ||
975 | anonymity, | ||
976 | expirationTime, | ||
977 | -2, 1, | ||
978 | GNUNET_CONSTANTS_SERVICE_TIMEOUT, | ||
979 | &sb_put_cont, | ||
980 | psc); | ||
981 | GNUNET_free (sb); | ||
982 | GNUNET_free (sb_enc); | ||
983 | } | ||
984 | |||
985 | |||
986 | /** | ||
987 | * Closure for 'process_update_node'. | ||
988 | */ | ||
989 | struct ProcessUpdateClosure | ||
990 | { | ||
991 | /** | ||
992 | * Function to call for each node. | ||
993 | */ | ||
994 | GNUNET_FS_IdentifierProcessor ip; | ||
995 | |||
996 | /** | ||
997 | * Closure for 'ip'. | ||
998 | */ | ||
999 | void *ip_cls; | ||
1000 | }; | ||
1001 | |||
1002 | |||
1003 | /** | ||
1004 | * Call the iterator in the closure for each node. | ||
1005 | * | ||
1006 | * @param cls closure (of type 'struct ProcessUpdateClosure *') | ||
1007 | * @param key current key code | ||
1008 | * @param value value in the hash map (of type 'struct NamespaceUpdateNode *') | ||
1009 | * @return GNUNET_YES if we should continue to | ||
1010 | * iterate, | ||
1011 | * GNUNET_NO if not. | ||
1012 | */ | ||
1013 | static int | ||
1014 | process_update_node (void *cls, | ||
1015 | const GNUNET_HashCode * key, | ||
1016 | void *value) | ||
1017 | { | ||
1018 | struct ProcessUpdateClosure *pc = cls; | ||
1019 | struct NamespaceUpdateNode *nsn = value; | ||
1020 | |||
1021 | pc->ip (pc->ip_cls, | ||
1022 | nsn->id, | ||
1023 | nsn->uri, | ||
1024 | nsn->md, | ||
1025 | nsn->update); | ||
1026 | return GNUNET_YES; | ||
1027 | } | ||
1028 | |||
1029 | |||
1030 | /** | ||
1031 | * Closure for 'find_sccs'. | ||
1032 | */ | ||
1033 | struct FindSccClosure | ||
1034 | { | ||
1035 | /** | ||
1036 | * Namespace we are operating on. | ||
1037 | */ | ||
1038 | struct GNUNET_FS_Namespace *namespace; | ||
1039 | |||
1040 | /** | ||
1041 | * Array with 'head's of SCCs. | ||
1042 | */ | ||
1043 | struct NamespaceUpdateNode **scc_array; | ||
1044 | |||
1045 | /** | ||
1046 | * Size of 'scc_array' | ||
1047 | */ | ||
1048 | unsigned int scc_array_size; | ||
1049 | |||
1050 | /** | ||
1051 | * Current generational ID used. | ||
1052 | */ | ||
1053 | unsigned int nug; | ||
1054 | |||
1055 | /** | ||
1056 | * Identifier for the current SCC, or UINT_MAX for none yet. | ||
1057 | */ | ||
1058 | unsigned int id; | ||
1059 | }; | ||
1060 | |||
1061 | |||
1062 | /** | ||
1063 | * Find all nodes reachable from the current node (including the | ||
1064 | * current node itself). If they are in no SCC, add them to the | ||
1065 | * current one. If they are the head of another SCC, merge the | ||
1066 | * SCCs. If they are in the middle of another SCC, let them be. | ||
1067 | * We can tell that a node is already in an SCC by checking if | ||
1068 | * its 'nug' field is set to the current 'nug' value. It is the | ||
1069 | * head of an SCC if it is in the 'scc_array' under its respective | ||
1070 | * 'scc_id'. | ||
1071 | * | ||
1072 | * @param cls closure (of type 'struct FindSccClosure') | ||
1073 | * @param key current key code | ||
1074 | * @param value value in the hash map | ||
1075 | * @return GNUNET_YES if we should continue to | ||
1076 | * iterate, | ||
1077 | * GNUNET_NO if not. | ||
1078 | */ | ||
1079 | static int | ||
1080 | find_sccs (void *cls, | ||
1081 | const GNUNET_HashCode * key, | ||
1082 | void *value) | ||
1083 | { | ||
1084 | struct FindSccClosure *fc = cls; | ||
1085 | struct NamespaceUpdateNode *nsn = value; | ||
1086 | GNUNET_HashCode hc; | ||
1087 | |||
1088 | if (nsn->nug == fc->nug) | ||
1089 | { | ||
1090 | if (fc->scc_array[nsn->scc_id] != nsn) | ||
1091 | return GNUNET_YES; /* part of another SCC, end trace */ | ||
1092 | fc->scc_array[nsn->scc_id] = NULL; | ||
1093 | if (fc->id == UINT_MAX) | ||
1094 | fc->id = nsn->scc_id; /* take over ID */ | ||
1095 | } | ||
1096 | else | ||
1097 | { | ||
1098 | nsn->nug = fc->nug; | ||
1099 | /* trace */ | ||
1100 | GNUNET_CRYPTO_hash (nsn->update, | ||
1101 | strlen (nsn->update), | ||
1102 | &hc); | ||
1103 | GNUNET_CONTAINER_multihashmap_get_multiple (fc->namespace->update_map, | ||
1104 | &hc, | ||
1105 | &find_sccs, | ||
1106 | fc); | ||
1107 | } | ||
1108 | return GNUNET_YES; | ||
1109 | } | ||
1110 | |||
1111 | |||
1112 | /** | ||
1113 | * List all of the identifiers in the namespace for which we could | ||
1114 | * produce an update. Namespace updates form a graph where each node | ||
1115 | * has a name. Each node can have any number of URI/meta-data entries | ||
1116 | * which can each be linked to other nodes. Cycles are possible. | ||
1117 | * | ||
1118 | * Calling this function with "next_id" NULL will cause the library to | ||
1119 | * call "ip" with a root for each strongly connected component of the | ||
1120 | * graph (a root being a node from which all other nodes in the Scc | ||
1121 | * are reachable). | ||
1122 | * | ||
1123 | * Calling this function with "next_id" being the name of a node will | ||
1124 | * cause the library to call "ip" with all children of the node. Note | ||
1125 | * that cycles within an SCC are possible (including self-loops). | ||
513 | * | 1126 | * |
514 | * @param namespace namespace to inspect for updateable content | 1127 | * @param namespace namespace to inspect for updateable content |
1128 | * @param next_id ID to look for; use NULL to look for SCC roots | ||
515 | * @param ip function to call on each updateable identifier | 1129 | * @param ip function to call on each updateable identifier |
516 | * @param ip_cls closure for ip | 1130 | * @param ip_cls closure for ip |
517 | */ | 1131 | */ |
518 | void | 1132 | void |
519 | GNUNET_FS_namespace_list_updateable (struct GNUNET_FS_Namespace *namespace, | 1133 | GNUNET_FS_namespace_list_updateable (struct GNUNET_FS_Namespace *namespace, |
1134 | const char *next_id, | ||
520 | GNUNET_FS_IdentifierProcessor ip, | 1135 | GNUNET_FS_IdentifierProcessor ip, |
521 | void *ip_cls) | 1136 | void *ip_cls) |
522 | { | 1137 | { |
523 | GNUNET_break (0); | 1138 | unsigned int i; |
524 | } | 1139 | unsigned int nug; |
1140 | GNUNET_HashCode hc; | ||
1141 | struct NamespaceUpdateNode *nsn; | ||
1142 | struct ProcessUpdateClosure pc; | ||
1143 | struct FindSccClosure fc; | ||
525 | 1144 | ||
1145 | if (namespace->update_nodes == NULL) | ||
1146 | read_update_information_graph (namespace); | ||
1147 | if (namespace->update_nodes == NULL) | ||
1148 | return; /* no nodes */ | ||
1149 | if (namespace->update_map == NULL) | ||
1150 | { | ||
1151 | /* need to construct */ | ||
1152 | namespace->update_map = GNUNET_CONTAINER_multihashmap_create (2 + 3 * namespace->update_node_count / 4); | ||
1153 | for (i=0;i<namespace->update_node_count;i++) | ||
1154 | { | ||
1155 | nsn = namespace->update_nodes[i]; | ||
1156 | GNUNET_CRYPTO_hash (nsn->id, | ||
1157 | strlen (nsn->id), | ||
1158 | &hc); | ||
1159 | GNUNET_CONTAINER_multihashmap_put (namespace->update_map, | ||
1160 | &hc, | ||
1161 | nsn, | ||
1162 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
1163 | } | ||
1164 | } | ||
1165 | if (next_id != NULL) | ||
1166 | { | ||
1167 | GNUNET_CRYPTO_hash (next_id, | ||
1168 | strlen (next_id), | ||
1169 | &hc); | ||
1170 | pc.ip = ip; | ||
1171 | pc.ip_cls = ip_cls; | ||
1172 | GNUNET_CONTAINER_multihashmap_get_multiple (namespace->update_map, | ||
1173 | &hc, | ||
1174 | &process_update_node, | ||
1175 | &pc); | ||
1176 | return; | ||
1177 | } | ||
1178 | /* Find heads of SCCs in update graph */ | ||
1179 | nug = ++namespace->nug_gen; | ||
1180 | fc.scc_array = NULL; | ||
1181 | fc.scc_array_size = 0; | ||
1182 | |||
1183 | for (i=0;i<namespace->update_node_count;i++) | ||
1184 | { | ||
1185 | nsn = namespace->update_nodes[i]; | ||
1186 | if (nsn->nug == nug) | ||
1187 | continue; /* already placed in SCC */ | ||
1188 | GNUNET_CRYPTO_hash (nsn->update, | ||
1189 | strlen (nsn->update), | ||
1190 | &hc); | ||
1191 | nsn->nug = nug; | ||
1192 | fc.id = UINT_MAX; | ||
1193 | fc.nug = nug; | ||
1194 | fc.namespace = namespace; | ||
1195 | GNUNET_CONTAINER_multihashmap_get_multiple (namespace->update_map, | ||
1196 | &hc, | ||
1197 | &find_sccs, | ||
1198 | &fc); | ||
1199 | if (fc.id == UINT_MAX) | ||
1200 | { | ||
1201 | /* start new SCC */ | ||
1202 | for (fc.id=0;fc.id<fc.scc_array_size;fc.id++) | ||
1203 | { | ||
1204 | if (fc.scc_array[fc.id] == NULL) | ||
1205 | { | ||
1206 | fc.scc_array[fc.id] = nsn; | ||
1207 | nsn->scc_id = fc.id; | ||
1208 | break; | ||
1209 | } | ||
1210 | } | ||
1211 | if (fc.id == fc.scc_array_size) | ||
1212 | { | ||
1213 | GNUNET_array_append (fc.scc_array, | ||
1214 | fc.scc_array_size, | ||
1215 | nsn); | ||
1216 | nsn->scc_id = fc.id; | ||
1217 | } | ||
1218 | /* put all nodes with same identifier into this SCC */ | ||
1219 | GNUNET_CRYPTO_hash (nsn->id, | ||
1220 | strlen (nsn->id), | ||
1221 | &hc); | ||
1222 | fc.id = nsn->scc_id; | ||
1223 | fc.nug = nug; | ||
1224 | fc.namespace = namespace; | ||
1225 | GNUNET_CONTAINER_multihashmap_get_multiple (namespace->update_map, | ||
1226 | &hc, | ||
1227 | &find_sccs, | ||
1228 | &fc); | ||
1229 | } | ||
1230 | else | ||
1231 | { | ||
1232 | /* make head of SCC "id" */ | ||
1233 | fc.scc_array[fc.id] = nsn; | ||
1234 | nsn->scc_id = fc.id; | ||
1235 | } | ||
1236 | } | ||
1237 | for (i=0;i<fc.scc_array_size;i++) | ||
1238 | { | ||
1239 | nsn = fc.scc_array[i]; | ||
1240 | ip (ip_cls, | ||
1241 | nsn->id, | ||
1242 | nsn->uri, | ||
1243 | nsn->md, | ||
1244 | nsn->update); | ||
1245 | } | ||
1246 | GNUNET_array_grow (fc.scc_array, | ||
1247 | fc.scc_array_size, | ||
1248 | 0); | ||
1249 | } | ||
526 | 1250 | ||
527 | 1251 | ||
528 | /* end of fs_namespace.c */ | 1252 | /* end of fs_namespace.c */ |