diff options
Diffstat (limited to 'src/fs/fs_namespace.c')
-rw-r--r-- | src/fs/fs_namespace.c | 818 |
1 files changed, 0 insertions, 818 deletions
diff --git a/src/fs/fs_namespace.c b/src/fs/fs_namespace.c deleted file mode 100644 index 155486be5..000000000 --- a/src/fs/fs_namespace.c +++ /dev/null | |||
@@ -1,818 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2003-2013 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your 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 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file fs/fs_namespace.c | ||
23 | * @brief publishing to namespaces, and tracking updateable entries | ||
24 | * for our namespaces | ||
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_publish_ublock.h" | ||
34 | |||
35 | |||
36 | /** | ||
37 | * Information about an (updateable) node in the | ||
38 | * namespace. | ||
39 | */ | ||
40 | struct NamespaceUpdateNode | ||
41 | { | ||
42 | /** | ||
43 | * Identifier for this node. | ||
44 | */ | ||
45 | char *id; | ||
46 | |||
47 | /** | ||
48 | * Identifier of children of this node. | ||
49 | */ | ||
50 | char *update; | ||
51 | |||
52 | /** | ||
53 | * Metadata for this entry. | ||
54 | */ | ||
55 | struct GNUNET_CONTAINER_MetaData *md; | ||
56 | |||
57 | /** | ||
58 | * URI of this entry in the namespace. | ||
59 | */ | ||
60 | struct GNUNET_FS_Uri *uri; | ||
61 | |||
62 | /** | ||
63 | * Namespace update generation ID. Used to ensure | ||
64 | * freshness of the tree_id. | ||
65 | */ | ||
66 | unsigned int nug; | ||
67 | |||
68 | /** | ||
69 | * TREE this entry belongs to (if nug is current). | ||
70 | */ | ||
71 | unsigned int tree_id; | ||
72 | }; | ||
73 | |||
74 | |||
75 | /** | ||
76 | * Handle to update information for a namespace. | ||
77 | */ | ||
78 | struct GNUNET_FS_UpdateInformationGraph | ||
79 | { | ||
80 | /** | ||
81 | * Handle to the FS service context. | ||
82 | */ | ||
83 | struct GNUNET_FS_Handle *h; | ||
84 | |||
85 | /** | ||
86 | * Array with information about nodes in the namespace. | ||
87 | */ | ||
88 | struct NamespaceUpdateNode **update_nodes; | ||
89 | |||
90 | /** | ||
91 | * Private key for the namespace. | ||
92 | */ | ||
93 | struct GNUNET_CRYPTO_EcdsaPrivateKey ns; | ||
94 | |||
95 | /** | ||
96 | * Hash map mapping identifiers of update nodes | ||
97 | * to the update nodes (initialized on-demand). | ||
98 | */ | ||
99 | struct GNUNET_CONTAINER_MultiHashMap *update_map; | ||
100 | |||
101 | /** | ||
102 | * Size of the update nodes array. | ||
103 | */ | ||
104 | unsigned int update_node_count; | ||
105 | |||
106 | /** | ||
107 | * Reference counter. | ||
108 | */ | ||
109 | unsigned int rc; | ||
110 | |||
111 | /** | ||
112 | * Generator for unique nug numbers. | ||
113 | */ | ||
114 | unsigned int nug_gen; | ||
115 | }; | ||
116 | |||
117 | |||
118 | /** | ||
119 | * Return the name of the directory in which we store | ||
120 | * the update information graph for the given local namespace. | ||
121 | * | ||
122 | * @param h file-sharing handle | ||
123 | * @param ns namespace handle | ||
124 | * @return NULL on error, otherwise the name of the directory | ||
125 | */ | ||
126 | static char * | ||
127 | get_update_information_directory ( | ||
128 | struct GNUNET_FS_Handle *h, | ||
129 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns) | ||
130 | { | ||
131 | char *dn; | ||
132 | char *ret; | ||
133 | struct GNUNET_CRYPTO_EcdsaPublicKey pub; | ||
134 | struct GNUNET_HashCode hc; | ||
135 | struct GNUNET_CRYPTO_HashAsciiEncoded enc; | ||
136 | |||
137 | if (GNUNET_OK != | ||
138 | GNUNET_CONFIGURATION_get_value_filename (h->cfg, "FS", "UPDATE_DIR", &dn)) | ||
139 | { | ||
140 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "fs", "UPDATE_DIR"); | ||
141 | return NULL; | ||
142 | } | ||
143 | GNUNET_CRYPTO_ecdsa_key_get_public (ns, &pub); | ||
144 | GNUNET_CRYPTO_hash (&pub, sizeof(pub), &hc); | ||
145 | GNUNET_CRYPTO_hash_to_enc (&hc, &enc); | ||
146 | GNUNET_asprintf (&ret, | ||
147 | "%s%s%s", | ||
148 | dn, | ||
149 | DIR_SEPARATOR_STR, | ||
150 | (const char *) enc.encoding); | ||
151 | GNUNET_free (dn); | ||
152 | return ret; | ||
153 | } | ||
154 | |||
155 | |||
156 | /** | ||
157 | * Release memory occupied by UIG datastructure. | ||
158 | * | ||
159 | * @param uig data structure to free | ||
160 | */ | ||
161 | static void | ||
162 | free_update_information_graph (struct GNUNET_FS_UpdateInformationGraph *uig) | ||
163 | { | ||
164 | unsigned int i; | ||
165 | struct NamespaceUpdateNode *nsn; | ||
166 | |||
167 | for (i = 0; i < uig->update_node_count; i++) | ||
168 | { | ||
169 | nsn = uig->update_nodes[i]; | ||
170 | GNUNET_CONTAINER_meta_data_destroy (nsn->md); | ||
171 | GNUNET_FS_uri_destroy (nsn->uri); | ||
172 | GNUNET_free (nsn->id); | ||
173 | GNUNET_free (nsn->update); | ||
174 | GNUNET_free (nsn); | ||
175 | } | ||
176 | GNUNET_array_grow (uig->update_nodes, uig->update_node_count, 0); | ||
177 | if (NULL != uig->update_map) | ||
178 | GNUNET_CONTAINER_multihashmap_destroy (uig->update_map); | ||
179 | GNUNET_free (uig); | ||
180 | } | ||
181 | |||
182 | |||
183 | /** | ||
184 | * Write a namespace's update node graph to a file. | ||
185 | * | ||
186 | * @param uig update information graph to dump | ||
187 | */ | ||
188 | static void | ||
189 | write_update_information_graph (struct GNUNET_FS_UpdateInformationGraph *uig) | ||
190 | { | ||
191 | char *fn; | ||
192 | struct GNUNET_BIO_WriteHandle *wh; | ||
193 | unsigned int i; | ||
194 | struct NamespaceUpdateNode *n; | ||
195 | char *uris; | ||
196 | |||
197 | fn = get_update_information_directory (uig->h, &uig->ns); | ||
198 | wh = GNUNET_BIO_write_open_file (fn); | ||
199 | if (NULL == wh) | ||
200 | { | ||
201 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
202 | _ ("Failed to open `%s' for writing: %s\n"), | ||
203 | fn, | ||
204 | strerror (errno)); | ||
205 | GNUNET_free (fn); | ||
206 | return; | ||
207 | } | ||
208 | if (GNUNET_OK != GNUNET_BIO_write_int32 (wh, | ||
209 | "fs-namespace-node-count", | ||
210 | uig->update_node_count)) | ||
211 | goto END; | ||
212 | for (i = 0; i < uig->update_node_count; i++) | ||
213 | { | ||
214 | n = uig->update_nodes[i]; | ||
215 | uris = GNUNET_FS_uri_to_string (n->uri); | ||
216 | struct GNUNET_BIO_WriteSpec ws[] = { | ||
217 | GNUNET_BIO_write_spec_string ("fs-namespace-node-id", n->id), | ||
218 | GNUNET_BIO_write_spec_meta_data ("fs-namespace-node-meta", n->md), | ||
219 | GNUNET_BIO_write_spec_string ("fs-namespace-node-update", n->update), | ||
220 | GNUNET_BIO_write_spec_string ("fs-namespace-uris", uris), | ||
221 | GNUNET_BIO_write_spec_end (), | ||
222 | }; | ||
223 | if (GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws)) | ||
224 | { | ||
225 | GNUNET_free (uris); | ||
226 | break; | ||
227 | } | ||
228 | GNUNET_free (uris); | ||
229 | } | ||
230 | END: | ||
231 | if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) | ||
232 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
233 | _ ("Failed to write `%s': %s\n"), | ||
234 | fn, | ||
235 | strerror (errno)); | ||
236 | GNUNET_free (fn); | ||
237 | } | ||
238 | |||
239 | |||
240 | /** | ||
241 | * Read the namespace update node graph from a file. | ||
242 | * | ||
243 | * @param h FS handle to use | ||
244 | * @param ns namespace to read | ||
245 | * @return update graph, never NULL | ||
246 | */ | ||
247 | static struct GNUNET_FS_UpdateInformationGraph * | ||
248 | read_update_information_graph (struct GNUNET_FS_Handle *h, | ||
249 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns) | ||
250 | { | ||
251 | struct GNUNET_FS_UpdateInformationGraph *uig; | ||
252 | char *fn; | ||
253 | struct GNUNET_BIO_ReadHandle *rh; | ||
254 | unsigned int i; | ||
255 | struct NamespaceUpdateNode *n; | ||
256 | char *uris; | ||
257 | uint32_t count; | ||
258 | char *emsg; | ||
259 | |||
260 | uig = GNUNET_new (struct GNUNET_FS_UpdateInformationGraph); | ||
261 | uig->h = h; | ||
262 | uig->ns = *ns; | ||
263 | fn = get_update_information_directory (h, ns); | ||
264 | if (GNUNET_YES != GNUNET_DISK_file_test (fn)) | ||
265 | { | ||
266 | GNUNET_free (fn); | ||
267 | return uig; | ||
268 | } | ||
269 | rh = GNUNET_BIO_read_open_file (fn); | ||
270 | if (NULL == rh) | ||
271 | { | ||
272 | GNUNET_free (fn); | ||
273 | return uig; | ||
274 | } | ||
275 | if (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "fs-namespace-count", | ||
276 | (int32_t *) &count)) | ||
277 | { | ||
278 | GNUNET_break (0); | ||
279 | goto END; | ||
280 | } | ||
281 | if (count > 1024 * 1024) | ||
282 | { | ||
283 | GNUNET_break (0); | ||
284 | goto END; | ||
285 | } | ||
286 | if (0 == count) | ||
287 | goto END; | ||
288 | uig->update_nodes = | ||
289 | GNUNET_malloc (count * sizeof(struct NamespaceUpdateNode *)); | ||
290 | |||
291 | for (i = 0; i < count; i++) | ||
292 | { | ||
293 | n = GNUNET_new (struct NamespaceUpdateNode); | ||
294 | struct GNUNET_BIO_ReadSpec rs[] = { | ||
295 | GNUNET_BIO_read_spec_string ("identifier", &n->id, 1024), | ||
296 | GNUNET_BIO_read_spec_meta_data ("meta", &n->md), | ||
297 | GNUNET_BIO_read_spec_string ("update-id", &n->update, 1024), | ||
298 | GNUNET_BIO_read_spec_string ("uri", &uris, 1024 * 2), | ||
299 | GNUNET_BIO_read_spec_end (), | ||
300 | }; | ||
301 | if (GNUNET_OK != GNUNET_BIO_read_spec_commit (rh, rs)) | ||
302 | { | ||
303 | GNUNET_break (0); | ||
304 | GNUNET_free (n->id); | ||
305 | GNUNET_free (n->update); | ||
306 | if (n->md != NULL) | ||
307 | GNUNET_CONTAINER_meta_data_destroy (n->md); | ||
308 | GNUNET_free (n); | ||
309 | break; | ||
310 | } | ||
311 | n->uri = GNUNET_FS_uri_parse (uris, &emsg); | ||
312 | GNUNET_free (uris); | ||
313 | if (n->uri == NULL) | ||
314 | { | ||
315 | GNUNET_break (0); | ||
316 | GNUNET_free (emsg); | ||
317 | GNUNET_free (n->id); | ||
318 | GNUNET_free (n->update); | ||
319 | GNUNET_CONTAINER_meta_data_destroy (n->md); | ||
320 | GNUNET_free (n); | ||
321 | break; | ||
322 | } | ||
323 | uig->update_nodes[i] = n; | ||
324 | } | ||
325 | uig->update_node_count = i; | ||
326 | END: | ||
327 | if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg)) | ||
328 | { | ||
329 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
330 | _ ("Failed to read `%s': %s\n"), | ||
331 | fn, | ||
332 | emsg); | ||
333 | GNUNET_free (emsg); | ||
334 | } | ||
335 | GNUNET_free (fn); | ||
336 | return uig; | ||
337 | } | ||
338 | |||
339 | |||
340 | /** | ||
341 | * Context for the SKS publication. | ||
342 | */ | ||
343 | struct GNUNET_FS_PublishSksContext | ||
344 | { | ||
345 | /** | ||
346 | * URI of the new entry in the namespace. | ||
347 | */ | ||
348 | struct GNUNET_FS_Uri *uri; | ||
349 | |||
350 | /** | ||
351 | * Namespace update node to add to namespace on success (or to be | ||
352 | * deleted if publishing failed). | ||
353 | */ | ||
354 | struct NamespaceUpdateNode *nsn; | ||
355 | |||
356 | /** | ||
357 | * Namespace we're publishing to. | ||
358 | */ | ||
359 | struct GNUNET_CRYPTO_EcdsaPrivateKey ns; | ||
360 | |||
361 | /** | ||
362 | * Handle to the datastore. | ||
363 | */ | ||
364 | struct GNUNET_DATASTORE_Handle *dsh; | ||
365 | |||
366 | /** | ||
367 | * Handle to FS. | ||
368 | */ | ||
369 | struct GNUNET_FS_Handle *h; | ||
370 | |||
371 | /** | ||
372 | * Function to call once we're done. | ||
373 | */ | ||
374 | GNUNET_FS_PublishContinuation cont; | ||
375 | |||
376 | /** | ||
377 | * Closure for cont. | ||
378 | */ | ||
379 | void *cont_cls; | ||
380 | |||
381 | /** | ||
382 | * Handle for our UBlock operation request. | ||
383 | */ | ||
384 | struct GNUNET_FS_PublishUblockContext *uc; | ||
385 | }; | ||
386 | |||
387 | |||
388 | /** | ||
389 | * Function called by the UBlock construction with | ||
390 | * the result from the PUT (UBlock) request. | ||
391 | * | ||
392 | * @param cls closure of type "struct GNUNET_FS_PublishSksContext*" | ||
393 | * @param msg error message (or NULL) | ||
394 | */ | ||
395 | static void | ||
396 | sks_publish_cont (void *cls, const char *msg) | ||
397 | { | ||
398 | struct GNUNET_FS_PublishSksContext *psc = cls; | ||
399 | struct GNUNET_FS_UpdateInformationGraph *uig; | ||
400 | |||
401 | psc->uc = NULL; | ||
402 | if (NULL != msg) | ||
403 | { | ||
404 | if (NULL != psc->cont) | ||
405 | psc->cont (psc->cont_cls, NULL, msg); | ||
406 | GNUNET_FS_publish_sks_cancel (psc); | ||
407 | return; | ||
408 | } | ||
409 | if (NULL != psc->nsn) | ||
410 | { | ||
411 | /* FIXME: this can be done much more | ||
412 | * efficiently by simply appending to the | ||
413 | * file and overwriting the 4-byte header */ | ||
414 | uig = read_update_information_graph (psc->h, &psc->ns); | ||
415 | GNUNET_array_append (uig->update_nodes, uig->update_node_count, psc->nsn); | ||
416 | psc->nsn = NULL; | ||
417 | write_update_information_graph (uig); | ||
418 | free_update_information_graph (uig); | ||
419 | } | ||
420 | if (NULL != psc->cont) | ||
421 | psc->cont (psc->cont_cls, psc->uri, NULL); | ||
422 | GNUNET_FS_publish_sks_cancel (psc); | ||
423 | } | ||
424 | |||
425 | |||
426 | /** | ||
427 | * Publish an SBlock on GNUnet. | ||
428 | * | ||
429 | * @param h handle to the file sharing subsystem | ||
430 | * @param ns namespace to publish in | ||
431 | * @param identifier identifier to use | ||
432 | * @param update update identifier to use | ||
433 | * @param meta metadata to use | ||
434 | * @param uri URI to refer to in the SBlock | ||
435 | * @param bo block options | ||
436 | * @param options publication options | ||
437 | * @param cont continuation | ||
438 | * @param cont_cls closure for cont | ||
439 | * @return NULL on error ('cont' will still be called) | ||
440 | */ | ||
441 | struct GNUNET_FS_PublishSksContext * | ||
442 | GNUNET_FS_publish_sks (struct GNUNET_FS_Handle *h, | ||
443 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns, | ||
444 | const char *identifier, | ||
445 | const char *update, | ||
446 | const struct GNUNET_CONTAINER_MetaData *meta, | ||
447 | const struct GNUNET_FS_Uri *uri, | ||
448 | const struct GNUNET_FS_BlockOptions *bo, | ||
449 | enum GNUNET_FS_PublishOptions options, | ||
450 | GNUNET_FS_PublishContinuation cont, | ||
451 | void *cont_cls) | ||
452 | { | ||
453 | struct GNUNET_FS_PublishSksContext *psc; | ||
454 | struct GNUNET_FS_Uri *sks_uri; | ||
455 | |||
456 | sks_uri = GNUNET_new (struct GNUNET_FS_Uri); | ||
457 | sks_uri->type = GNUNET_FS_URI_SKS; | ||
458 | sks_uri->data.sks.identifier = GNUNET_strdup (identifier); | ||
459 | GNUNET_CRYPTO_ecdsa_key_get_public (ns, &sks_uri->data.sks.ns); | ||
460 | |||
461 | psc = GNUNET_new (struct GNUNET_FS_PublishSksContext); | ||
462 | psc->h = h; | ||
463 | psc->uri = sks_uri; | ||
464 | psc->cont = cont; | ||
465 | psc->cont_cls = cont_cls; | ||
466 | psc->ns = *ns; | ||
467 | if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY)) | ||
468 | { | ||
469 | psc->dsh = GNUNET_DATASTORE_connect (h->cfg); | ||
470 | if (NULL == psc->dsh) | ||
471 | { | ||
472 | sks_publish_cont (psc, _ ("Failed to connect to datastore.")); | ||
473 | return NULL; | ||
474 | } | ||
475 | } | ||
476 | if (NULL != update) | ||
477 | { | ||
478 | psc->nsn = GNUNET_new (struct NamespaceUpdateNode); | ||
479 | psc->nsn->id = GNUNET_strdup (identifier); | ||
480 | psc->nsn->update = GNUNET_strdup (update); | ||
481 | psc->nsn->md = GNUNET_CONTAINER_meta_data_duplicate (meta); | ||
482 | psc->nsn->uri = GNUNET_FS_uri_dup (uri); | ||
483 | } | ||
484 | psc->uc = GNUNET_FS_publish_ublock_ (h, | ||
485 | psc->dsh, | ||
486 | identifier, | ||
487 | update, | ||
488 | ns, | ||
489 | meta, | ||
490 | uri, | ||
491 | bo, | ||
492 | options, | ||
493 | &sks_publish_cont, | ||
494 | psc); | ||
495 | return psc; | ||
496 | } | ||
497 | |||
498 | |||
499 | /** | ||
500 | * Abort the SKS publishing operation. | ||
501 | * | ||
502 | * @param psc context of the operation to abort. | ||
503 | */ | ||
504 | void | ||
505 | GNUNET_FS_publish_sks_cancel (struct GNUNET_FS_PublishSksContext *psc) | ||
506 | { | ||
507 | if (NULL != psc->uc) | ||
508 | { | ||
509 | GNUNET_FS_publish_ublock_cancel_ (psc->uc); | ||
510 | psc->uc = NULL; | ||
511 | } | ||
512 | if (NULL != psc->dsh) | ||
513 | { | ||
514 | GNUNET_DATASTORE_disconnect (psc->dsh, GNUNET_NO); | ||
515 | psc->dsh = NULL; | ||
516 | } | ||
517 | GNUNET_FS_uri_destroy (psc->uri); | ||
518 | if (NULL != psc->nsn) | ||
519 | { | ||
520 | GNUNET_CONTAINER_meta_data_destroy (psc->nsn->md); | ||
521 | GNUNET_FS_uri_destroy (psc->nsn->uri); | ||
522 | GNUNET_free (psc->nsn->id); | ||
523 | GNUNET_free (psc->nsn->update); | ||
524 | GNUNET_free (psc->nsn); | ||
525 | } | ||
526 | GNUNET_free (psc); | ||
527 | } | ||
528 | |||
529 | |||
530 | /** | ||
531 | * Closure for 'process_update_node'. | ||
532 | */ | ||
533 | struct ProcessUpdateClosure | ||
534 | { | ||
535 | /** | ||
536 | * Function to call for each node. | ||
537 | */ | ||
538 | GNUNET_FS_IdentifierProcessor ip; | ||
539 | |||
540 | /** | ||
541 | * Closure for 'ip'. | ||
542 | */ | ||
543 | void *ip_cls; | ||
544 | }; | ||
545 | |||
546 | |||
547 | /** | ||
548 | * Call the iterator in the closure for each node. | ||
549 | * | ||
550 | * @param cls closure (of type 'struct ProcessUpdateClosure *') | ||
551 | * @param key current key code | ||
552 | * @param value value in the hash map (of type 'struct NamespaceUpdateNode *') | ||
553 | * @return GNUNET_YES if we should continue to | ||
554 | * iterate, | ||
555 | * GNUNET_NO if not. | ||
556 | */ | ||
557 | static int | ||
558 | process_update_node (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
559 | { | ||
560 | struct ProcessUpdateClosure *pc = cls; | ||
561 | struct NamespaceUpdateNode *nsn = value; | ||
562 | |||
563 | pc->ip (pc->ip_cls, nsn->id, nsn->uri, nsn->md, nsn->update); | ||
564 | return GNUNET_YES; | ||
565 | } | ||
566 | |||
567 | |||
568 | /** | ||
569 | * Closure for 'find_trees'. | ||
570 | */ | ||
571 | struct FindTreeClosure | ||
572 | { | ||
573 | /** | ||
574 | * UIG we are operating on. | ||
575 | */ | ||
576 | struct GNUNET_FS_UpdateInformationGraph *uig; | ||
577 | |||
578 | /** | ||
579 | * Array with 'head's of TREEs. | ||
580 | */ | ||
581 | struct NamespaceUpdateNode **tree_array; | ||
582 | |||
583 | /** | ||
584 | * Size of 'tree_array' | ||
585 | */ | ||
586 | unsigned int tree_array_size; | ||
587 | |||
588 | /** | ||
589 | * Current generational ID used. | ||
590 | */ | ||
591 | unsigned int nug; | ||
592 | |||
593 | /** | ||
594 | * Identifier for the current TREE, or UINT_MAX for none yet. | ||
595 | */ | ||
596 | unsigned int id; | ||
597 | }; | ||
598 | |||
599 | |||
600 | /** | ||
601 | * Find all nodes reachable from the current node (including the | ||
602 | * current node itself). If they are in no tree, add them to the | ||
603 | * current one. If they are the head of another tree, merge the | ||
604 | * trees. If they are in the middle of another tree, let them be. | ||
605 | * We can tell that a node is already in an tree by checking if | ||
606 | * its 'nug' field is set to the current 'nug' value. It is the | ||
607 | * head of an tree if it is in the 'tree_array' under its respective | ||
608 | * 'tree_id'. | ||
609 | * | ||
610 | * In short, we're trying to find the smallest number of tree to | ||
611 | * cover a directed graph. | ||
612 | * | ||
613 | * @param cls closure (of type 'struct FindTreeClosure') | ||
614 | * @param key current key code | ||
615 | * @param value value in the hash map | ||
616 | * @return GNUNET_YES if we should continue to | ||
617 | * iterate, | ||
618 | * GNUNET_NO if not. | ||
619 | */ | ||
620 | static int | ||
621 | find_trees (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
622 | { | ||
623 | struct FindTreeClosure *fc = cls; | ||
624 | struct NamespaceUpdateNode *nsn = value; | ||
625 | struct GNUNET_HashCode hc; | ||
626 | |||
627 | if (nsn->nug == fc->nug) | ||
628 | { | ||
629 | if (UINT_MAX == nsn->tree_id) | ||
630 | return GNUNET_YES; /* circular */ | ||
631 | GNUNET_assert (nsn->tree_id < fc->tree_array_size); | ||
632 | if (fc->tree_array[nsn->tree_id] != nsn) | ||
633 | return GNUNET_YES; /* part of "another" (directed) TREE, | ||
634 | * and not root of it, end trace */ | ||
635 | if (nsn->tree_id == fc->id) | ||
636 | return GNUNET_YES; /* that's our own root (can this be?) */ | ||
637 | /* merge existing TREE, we have a root for both */ | ||
638 | fc->tree_array[nsn->tree_id] = NULL; | ||
639 | if (UINT_MAX == fc->id) | ||
640 | fc->id = nsn->tree_id; /* take over ID */ | ||
641 | } | ||
642 | else | ||
643 | { | ||
644 | nsn->nug = fc->nug; | ||
645 | nsn->tree_id = UINT_MAX; /* mark as undef */ | ||
646 | /* trace */ | ||
647 | GNUNET_CRYPTO_hash (nsn->update, strlen (nsn->update), &hc); | ||
648 | GNUNET_CONTAINER_multihashmap_get_multiple (fc->uig->update_map, | ||
649 | &hc, | ||
650 | &find_trees, | ||
651 | fc); | ||
652 | } | ||
653 | return GNUNET_YES; | ||
654 | } | ||
655 | |||
656 | |||
657 | /** | ||
658 | * List all of the identifiers in the namespace for which we could | ||
659 | * produce an update. Namespace updates form a graph where each node | ||
660 | * has a name. Each node can have any number of URI/meta-data entries | ||
661 | * which can each be linked to other nodes. Cycles are possible. | ||
662 | * | ||
663 | * Calling this function with "next_id" NULL will cause the library to | ||
664 | * call "ip" with a root for each strongly connected component of the | ||
665 | * graph (a root being a node from which all other nodes in the Tree | ||
666 | * are reachable). | ||
667 | * | ||
668 | * Calling this function with "next_id" being the name of a node will | ||
669 | * cause the library to call "ip" with all children of the node. Note | ||
670 | * that cycles within the final tree are possible (including self-loops). | ||
671 | * I know, odd definition of a tree, but the GUI will display an actual | ||
672 | * tree (GtkTreeView), so that's what counts for the term here. | ||
673 | * | ||
674 | * @param h fs handle to use | ||
675 | * @param ns namespace to inspect for updateable content | ||
676 | * @param next_id ID to look for; use NULL to look for tree roots | ||
677 | * @param ip function to call on each updateable identifier | ||
678 | * @param ip_cls closure for ip | ||
679 | */ | ||
680 | void | ||
681 | GNUNET_FS_namespace_list_updateable ( | ||
682 | struct GNUNET_FS_Handle *h, | ||
683 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns, | ||
684 | const char *next_id, | ||
685 | GNUNET_FS_IdentifierProcessor ip, | ||
686 | void *ip_cls) | ||
687 | { | ||
688 | unsigned int i; | ||
689 | unsigned int nug; | ||
690 | struct GNUNET_HashCode hc; | ||
691 | struct NamespaceUpdateNode *nsn; | ||
692 | struct ProcessUpdateClosure pc; | ||
693 | struct FindTreeClosure fc; | ||
694 | struct GNUNET_FS_UpdateInformationGraph *uig; | ||
695 | |||
696 | uig = read_update_information_graph (h, ns); | ||
697 | if (NULL == uig->update_nodes) | ||
698 | { | ||
699 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
700 | "No updateable nodes found for ID `%s'\n", | ||
701 | next_id); | ||
702 | free_update_information_graph (uig); | ||
703 | return; /* no nodes */ | ||
704 | } | ||
705 | uig->update_map = | ||
706 | GNUNET_CONTAINER_multihashmap_create (2 + 3 * uig->update_node_count / 4, | ||
707 | GNUNET_NO); | ||
708 | for (i = 0; i < uig->update_node_count; i++) | ||
709 | { | ||
710 | nsn = uig->update_nodes[i]; | ||
711 | GNUNET_CRYPTO_hash (nsn->id, strlen (nsn->id), &hc); | ||
712 | GNUNET_CONTAINER_multihashmap_put ( | ||
713 | uig->update_map, | ||
714 | &hc, | ||
715 | nsn, | ||
716 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
717 | } | ||
718 | if (NULL != next_id) | ||
719 | { | ||
720 | GNUNET_CRYPTO_hash (next_id, strlen (next_id), &hc); | ||
721 | pc.ip = ip; | ||
722 | pc.ip_cls = ip_cls; | ||
723 | GNUNET_CONTAINER_multihashmap_get_multiple (uig->update_map, | ||
724 | &hc, | ||
725 | &process_update_node, | ||
726 | &pc); | ||
727 | free_update_information_graph (uig); | ||
728 | return; | ||
729 | } | ||
730 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
731 | "Calculating TREEs to find roots of update trees\n"); | ||
732 | /* Find heads of TREEs in update graph */ | ||
733 | nug = ++uig->nug_gen; | ||
734 | fc.tree_array = NULL; | ||
735 | fc.tree_array_size = 0; | ||
736 | |||
737 | for (i = 0; i < uig->update_node_count; i++) | ||
738 | { | ||
739 | nsn = uig->update_nodes[i]; | ||
740 | if (nsn->nug == nug) | ||
741 | { | ||
742 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
743 | "TREE of node `%s' is %u\n", | ||
744 | nsn->id, | ||
745 | nsn->nug); | ||
746 | continue; /* already placed in TREE */ | ||
747 | } | ||
748 | GNUNET_CRYPTO_hash (nsn->update, strlen (nsn->update), &hc); | ||
749 | nsn->nug = nug; | ||
750 | nsn->tree_id = UINT_MAX; | ||
751 | fc.id = UINT_MAX; | ||
752 | fc.nug = nug; | ||
753 | fc.uig = uig; | ||
754 | GNUNET_CONTAINER_multihashmap_get_multiple (uig->update_map, | ||
755 | &hc, | ||
756 | &find_trees, | ||
757 | &fc); | ||
758 | if (UINT_MAX == fc.id) | ||
759 | { | ||
760 | /* start new TREE */ | ||
761 | for (fc.id = 0; fc.id < fc.tree_array_size; fc.id++) | ||
762 | { | ||
763 | if (NULL == fc.tree_array[fc.id]) | ||
764 | { | ||
765 | fc.tree_array[fc.id] = nsn; | ||
766 | nsn->tree_id = fc.id; | ||
767 | break; | ||
768 | } | ||
769 | } | ||
770 | if (fc.id == fc.tree_array_size) | ||
771 | { | ||
772 | GNUNET_array_append (fc.tree_array, fc.tree_array_size, nsn); | ||
773 | nsn->tree_id = fc.id; | ||
774 | } | ||
775 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
776 | "Starting new TREE %u with node `%s'\n", | ||
777 | nsn->tree_id, | ||
778 | nsn->id); | ||
779 | /* put all nodes with same identifier into this TREE */ | ||
780 | GNUNET_CRYPTO_hash (nsn->id, strlen (nsn->id), &hc); | ||
781 | fc.id = nsn->tree_id; | ||
782 | fc.nug = nug; | ||
783 | fc.uig = uig; | ||
784 | GNUNET_CONTAINER_multihashmap_get_multiple (uig->update_map, | ||
785 | &hc, | ||
786 | &find_trees, | ||
787 | &fc); | ||
788 | } | ||
789 | else | ||
790 | { | ||
791 | /* make head of TREE "id" */ | ||
792 | fc.tree_array[fc.id] = nsn; | ||
793 | nsn->tree_id = fc.id; | ||
794 | } | ||
795 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
796 | "TREE of node `%s' is %u\n", | ||
797 | nsn->id, | ||
798 | fc.id); | ||
799 | } | ||
800 | for (i = 0; i < fc.tree_array_size; i++) | ||
801 | { | ||
802 | nsn = fc.tree_array[i]; | ||
803 | if (NULL != nsn) | ||
804 | { | ||
805 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
806 | "Root of TREE %u is node `%s'\n", | ||
807 | i, | ||
808 | nsn->id); | ||
809 | ip (ip_cls, nsn->id, nsn->uri, nsn->md, nsn->update); | ||
810 | } | ||
811 | } | ||
812 | GNUNET_array_grow (fc.tree_array, fc.tree_array_size, 0); | ||
813 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Done processing TREEs\n"); | ||
814 | free_update_information_graph (uig); | ||
815 | } | ||
816 | |||
817 | |||
818 | /* end of fs_namespace.c */ | ||