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