From 046e90172489ca6091346685bf444a037b1187ee Mon Sep 17 00:00:00 2001 From: LRN Date: Tue, 19 Feb 2013 04:04:35 +0000 Subject: [PATCH] Asynchronous namespace creation. With a test. --- src/fs/fs_namespace.c | 149 ++++++++++++++++++++++++++++++++ src/fs/test_fs_namespace.c | 149 ++++++++++++++++++++++++++------ src/include/gnunet_fs_service.h | 39 +++++++++ 3 files changed, 310 insertions(+), 27 deletions(-) diff --git a/src/fs/fs_namespace.c b/src/fs/fs_namespace.c index f9cee5ff9..46121aa9c 100644 --- a/src/fs/fs_namespace.c +++ b/src/fs/fs_namespace.c @@ -37,6 +37,43 @@ #define MAX_SBLOCK_SIZE (60 * 1024) +/** + * Context for creating a namespace asynchronously. + */ +struct GNUNET_FS_NamespaceCreationContext +{ + /** + * Context for asynchronous key creation. + */ + struct GNUNET_CRYPTO_RsaKeyGenerationContext *keycreator; + + /** + * Name of the file to store key in / read key from. + */ + char *filename; + + /** + * Name of the namespace. + */ + char *name; + + /** + * Global fs handle + */ + struct GNUNET_FS_Handle *h; + + /** + * Function to call when generation ends (successfully or not) + */ + GNUNET_FS_NamespaceCreationCallback cont; + + /** + * Client value to pass to continuation function. + */ + void *cont_cls; +}; + + /** * Return the name of the directory in which we store * our local namespaces (or rather, their public keys). @@ -267,6 +304,118 @@ GNUNET_FS_namespace_create (struct GNUNET_FS_Handle *h, const char *name) } +/** + * Function called upon completion of 'GNUNET_CRYPTO_rsa_key_create_start'. + * + * @param cls closure + * @param pk NULL on error, otherwise the private key (which must be free'd by the callee) + * @param emsg NULL on success, otherwise an error message + */ +static void +ns_key_created (void *cls, struct GNUNET_CRYPTO_RsaPrivateKey *pk, + const char *emsg) +{ + struct GNUNET_FS_NamespaceCreationContext *ncc = cls; + + ncc->keycreator = NULL; + + if (pk) + { + struct GNUNET_FS_Namespace *ret; + ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Namespace)); + ret->rc = 1; + ret->key = pk; + ret->h = ncc->h; + ret->name = ncc->name; + ret->filename = ncc->filename; + ncc->cont (ncc->cont_cls, ret, NULL); + } + else + { + GNUNET_free (ncc->filename); + GNUNET_free (ncc->name); + ncc->cont (ncc->cont_cls, NULL, emsg); + } + GNUNET_free (ncc); +} + + +/** + * Create a namespace with the given name. + * If one already exists, the continuation will be called with a handle to + * the existing namespace. + * Otherwise creates a new namespace. + * + * @param h handle to the file sharing subsystem + * @param name name to use for the namespace + * @return namespace creation context, NULL on error (i.e. invalid filename) + */ +struct GNUNET_FS_NamespaceCreationContext * +GNUNET_FS_namespace_create_start (struct GNUNET_FS_Handle *h, const char *name, + GNUNET_FS_NamespaceCreationCallback cont, void *cont_cls) +{ + char *dn; + char *fn; + struct GNUNET_FS_NamespaceCreationContext *ret; + + dn = get_namespace_directory (h); + if (NULL == dn) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Can't determine where namespace directory is\n")); + return NULL; + } + GNUNET_asprintf (&fn, "%s%s%s", dn, DIR_SEPARATOR_STR, name); + GNUNET_free (dn); + + ret = GNUNET_malloc (sizeof (struct GNUNET_FS_NamespaceCreationContext)); + ret->filename = fn; + ret->h = h; + ret->name = GNUNET_strdup (name); + ret->cont = cont; + ret->cont_cls = cont_cls; + + ret->keycreator = GNUNET_CRYPTO_rsa_key_create_start (fn, + ns_key_created, ret); + + if (NULL == ret->keycreator) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to start creating or reading private key for namespace `%s'\n"), + name); + GNUNET_free (fn); + GNUNET_free (ret->name); + GNUNET_free (ret); + return NULL; + } + return ret; +} + + +/** + * Abort namespace creation. + * + * @param ncc namespace creation context to abort + */ +void +GNUNET_FS_namespace_create_stop (struct GNUNET_FS_NamespaceCreationContext *ncc) +{ + if (NULL != ncc->keycreator) + { + GNUNET_CRYPTO_rsa_key_create_stop (ncc->keycreator); + ncc->keycreator = NULL; + } + if (NULL != ncc->filename) + { + if (0 != UNLINK (ncc->filename)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", ncc->filename); + GNUNET_free (ncc->filename); + } + GNUNET_free_non_null (ncc->name); + GNUNET_free (ncc); +} + + /** * Duplicate a namespace handle. * diff --git a/src/fs/test_fs_namespace.c b/src/fs/test_fs_namespace.c index e4ac4931e..61f304487 100644 --- a/src/fs/test_fs_namespace.c +++ b/src/fs/test_fs_namespace.c @@ -43,27 +43,64 @@ static struct GNUNET_FS_SearchContext *ksk_search; static GNUNET_SCHEDULER_TaskIdentifier kill_task; +static GNUNET_SCHEDULER_TaskIdentifier kill_ncc_task; + +struct GNUNET_FS_NamespaceCreationContext *ncc; + static int update_started; static int err; +static int phase; + +const struct GNUNET_CONFIGURATION_Handle *config; + +static void ns_created (void *cls, struct GNUNET_FS_Namespace *ns, const char *emsg); + +static void do_ncc_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + static void -abort_ksk_search_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +next_phase () { - if (ksk_search != NULL) + switch (phase) { - GNUNET_FS_search_stop (ksk_search); - ksk_search = NULL; - if (sks_search == NULL) + case 0: + phase += 1; + FPRINTF (stderr, "%s", "Testing asynchronous namespace creation\n"); + ncc = GNUNET_FS_namespace_create_start (fs, "testNamespace", ns_created, NULL); + if (NULL == ncc) { - GNUNET_FS_stop (fs); - if (GNUNET_SCHEDULER_NO_TASK != kill_task) - GNUNET_SCHEDULER_cancel (kill_task); + FPRINTF (stderr, "%s", "Failed to start asynchronous namespace creation\n"); + err = 1; + next_phase (); + return; } + kill_ncc_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, &do_ncc_timeout, + NULL); + break; + case 1: + FPRINTF (stderr, "%s", "Shutting down FS\n"); + GNUNET_FS_stop (fs); + if (GNUNET_SCHEDULER_NO_TASK != kill_task) + GNUNET_SCHEDULER_cancel (kill_task); + kill_task = GNUNET_SCHEDULER_NO_TASK; } } +static void +abort_ksk_search_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (NULL == ksk_search) + return; + FPRINTF (stderr, "%s", "Stopping KSK search\n"); + GNUNET_FS_search_stop (ksk_search); + ksk_search = NULL; + if (sks_search == NULL) + next_phase (); +} + static void abort_sks_search_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) @@ -72,17 +109,14 @@ abort_sks_search_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) if (sks_search == NULL) return; + FPRINTF (stderr, "%s", "Stopping SKS search\n"); GNUNET_FS_search_stop (sks_search); sks_search = NULL; ns = GNUNET_FS_namespace_create (fs, "testNamespace"); GNUNET_assert (NULL != ns); GNUNET_assert (GNUNET_OK == GNUNET_FS_namespace_delete (ns, GNUNET_YES)); if (ksk_search == NULL) - { - GNUNET_FS_stop (fs); - if (GNUNET_SCHEDULER_NO_TASK != kill_task) - GNUNET_SCHEDULER_cancel (kill_task); - } + next_phase (); } @@ -99,18 +133,25 @@ do_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) static void * progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event) { + char *got; switch (event->status) { case GNUNET_FS_STATUS_SEARCH_RESULT: + got = GNUNET_FS_uri_to_string (event->value.search.specifics.result.uri); + FPRINTF (stderr, "Got a search result `%s'\n", got); if (sks_search == event->value.search.sc) { if (!GNUNET_FS_uri_test_equal (sks_expect_uri, event->value.search.specifics.result.uri)) { - FPRINTF (stderr, "%s", "Wrong result for sks search!\n"); + char *expected; + expected = GNUNET_FS_uri_to_string (sks_expect_uri); + FPRINTF (stderr, "Wrong result for sks search! Expected:\n%s\nGot:\n%s\n", expected, got); + GNUNET_free (expected); err = 1; } /* give system 1ms to initiate update search! */ + FPRINTF (stderr, "scheduling `%s'\n", "abort_sks_search_task"); GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS, &abort_sks_search_task, NULL); } @@ -119,17 +160,22 @@ progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event) if (!GNUNET_FS_uri_test_equal (ksk_expect_uri, event->value.search.specifics.result.uri)) { - FPRINTF (stderr, "%s", "Wrong result for ksk search!\n"); + char *expected; + expected = GNUNET_FS_uri_to_string (ksk_expect_uri); + FPRINTF (stderr, "Wrong result for ksk search! Expected:\n%s\nGot:\n%s\n", expected, got); + GNUNET_free (expected); err = 1; } + FPRINTF (stderr, "scheduling `%s'\n", "abort_ksk_search_task"); GNUNET_SCHEDULER_add_continuation (&abort_ksk_search_task, NULL, GNUNET_SCHEDULER_REASON_PREREQ_DONE); } else { - FPRINTF (stderr, "%s", "Unexpected search result received!\n"); + FPRINTF (stderr, "Unexpected search result `%s' received!\n", got); GNUNET_break (0); } + GNUNET_free (got); break; case GNUNET_FS_STATUS_SEARCH_ERROR: FPRINTF (stderr, "Error searching file: %s\n", @@ -144,6 +190,7 @@ progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event) GNUNET_break (0); break; case GNUNET_FS_STATUS_SEARCH_START: + FPRINTF (stderr, "Search %s started\n", event->value.search.pctx); GNUNET_assert ((NULL == event->value.search.cctx) || (0 == strcmp ("sks_search", event->value.search.cctx)) || (0 == strcmp ("ksk_search", event->value.search.cctx))); @@ -155,8 +202,10 @@ progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *event) GNUNET_assert (1 == event->value.search.anonymity); break; case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED: + FPRINTF (stderr, "%s", "Search result stopped\n"); return NULL; case GNUNET_FS_STATUS_SEARCH_STOPPED: + FPRINTF (stderr, "%s", "Search stopped\n"); return NULL; default: FPRINTF (stderr, "Unexpected event: %d\n", event->status); @@ -176,11 +225,12 @@ publish_cont (void *cls, const struct GNUNET_FS_Uri *ksk_uri, const char *emsg) if (NULL != emsg) { - FPRINTF (stderr, "Error publishing: %s\n", emsg); + FPRINTF (stderr, "Error publishing ksk: %s\n", emsg); err = 1; GNUNET_FS_stop (fs); return; } + FPRINTF (stderr, "%s", "Published ksk\n"); GNUNET_CRYPTO_hash_to_enc (&nsid, &enc); GNUNET_snprintf (sbuf, sizeof (sbuf), "gnunet://fs/sks/%s/this", &enc); sks_uri = GNUNET_FS_uri_parse (sbuf, &msg); @@ -192,6 +242,7 @@ publish_cont (void *cls, const struct GNUNET_FS_Uri *ksk_uri, const char *emsg) GNUNET_free_non_null (msg); return; } + FPRINTF (stderr, "%s", "Starting searches\n"); ksk_search = GNUNET_FS_search_start (fs, ksk_uri, 1, GNUNET_FS_SEARCH_OPTION_NONE, "ksk_search"); @@ -212,11 +263,12 @@ sks_cont (void *cls, const struct GNUNET_FS_Uri *uri, const char *emsg) if (NULL == uri) { - fprintf (stderr, "Error publishing: %s\n", emsg); + fprintf (stderr, "Error publishing sks: %s\n", emsg); err = 1; GNUNET_FS_stop (fs); return; } + FPRINTF (stderr, "%s", "Published sks\n"); meta = GNUNET_CONTAINER_meta_data_create (); msg = NULL; ksk_uri = GNUNET_FS_uri_parse ("gnunet://fs/ksk/ns-search", &msg); @@ -243,11 +295,12 @@ adv_cont (void *cls, const struct GNUNET_FS_Uri *uri, const char *emsg) if (NULL != emsg) { - FPRINTF (stderr, "Error publishing: %s\n", emsg); + FPRINTF (stderr, "Error advertising: %s\n", emsg); err = 1; GNUNET_FS_stop (fs); return; } + FPRINTF (stderr, "%s", "Created an advertising\n"); ns = GNUNET_FS_namespace_create (fs, "testNamespace"); GNUNET_assert (NULL != ns); meta = GNUNET_CONTAINER_meta_data_create (); @@ -270,24 +323,22 @@ ns_iterator (void *cls, const char *name, const struct GNUNET_HashCode * id) { int *ok = cls; + FPRINTF (stderr, "Namespace in the list: %s\n", name); if (0 != strcmp (name, "testNamespace")) return; *ok = GNUNET_YES; nsid = *id; } - static void -testNamespace () +testCreatedNamespace (struct GNUNET_FS_Namespace *ns) { - struct GNUNET_FS_Namespace *ns; struct GNUNET_FS_BlockOptions bo; struct GNUNET_CONTAINER_MetaData *meta; struct GNUNET_FS_Uri *ksk_uri; int ok; - ns = GNUNET_FS_namespace_create (fs, "testNamespace"); - GNUNET_assert (NULL != ns); + FPRINTF (stderr, "%s", "Listing namespaces\n"); ok = GNUNET_NO; GNUNET_FS_namespace_list (fs, &ns_iterator, &ok); if (GNUNET_NO == ok) @@ -298,6 +349,7 @@ testNamespace () err = 1; return; } + FPRINTF (stderr, "%s", "Creating an advertising\n"); meta = GNUNET_CONTAINER_meta_data_create (); ksk_uri = GNUNET_FS_uri_parse ("gnunet://fs/ksk/testnsa", NULL); bo.content_priority = 1; @@ -307,22 +359,65 @@ testNamespace () GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES); GNUNET_FS_namespace_advertise (fs, ksk_uri, ns, meta, &bo, "root", &adv_cont, NULL); - kill_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, &do_timeout, - NULL); GNUNET_FS_uri_destroy (ksk_uri); GNUNET_FS_namespace_delete (ns, GNUNET_NO); GNUNET_CONTAINER_meta_data_destroy (meta); } +static void +do_ncc_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + FPRINTF (stderr, "%s", "Asynchronous NS creation timed out\n"); + kill_ncc_task = GNUNET_SCHEDULER_NO_TASK; + if (NULL == ncc) + return; + GNUNET_FS_namespace_create_stop (ncc); + ncc = NULL; + err = 1; +} + +static void +ns_created (void *cls, struct GNUNET_FS_Namespace *ns, const char *emsg) +{ + if (GNUNET_SCHEDULER_NO_TASK != kill_ncc_task) + GNUNET_SCHEDULER_cancel (kill_ncc_task); + kill_ncc_task = GNUNET_SCHEDULER_NO_TASK; + if (NULL == ns) + { + FPRINTF (stderr, "Asynchronous NS creation failed: %s\n", emsg); + err = 1; + return; + } + + FPRINTF (stderr, "%s", "Namespace created asynchronously\n"); + testCreatedNamespace (ns); +} + +static void +testNamespace () +{ + struct GNUNET_FS_Namespace *ns; + + FPRINTF (stderr, "%s", "Testing synchronous namespace creation\n"); + ns = GNUNET_FS_namespace_create (fs, "testNamespace"); + GNUNET_assert (NULL != ns); + testCreatedNamespace (ns); + + kill_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, &do_timeout, + NULL); +} + static void run (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, struct GNUNET_TESTING_Peer *peer) { + config = cfg; fs = GNUNET_FS_start (cfg, "test-fs-namespace", &progress_cb, NULL, GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END); + phase = 0; testNamespace (); } diff --git a/src/include/gnunet_fs_service.h b/src/include/gnunet_fs_service.h index a80662867..d6b26d927 100644 --- a/src/include/gnunet_fs_service.h +++ b/src/include/gnunet_fs_service.h @@ -2239,6 +2239,45 @@ struct GNUNET_FS_Namespace * GNUNET_FS_namespace_create (struct GNUNET_FS_Handle *h, const char *name); +/** + * Context for creating a namespace asynchronously. + */ +struct GNUNET_FS_NamespaceCreationContext; + +/** + * Function called upon completion of 'GNUNET_FS_namespace_create_start'. + * + * @param cls closure + * @param ns NULL on error, otherwise the namespace (which must be free'd by the callee) + * @param emsg NULL on success, otherwise an error message + */ +typedef void (*GNUNET_FS_NamespaceCreationCallback)(void *cls, + struct GNUNET_FS_Namespace *ns, const char *emsg); + + +/** + * Create a namespace with the given name; if one already + * exists, return a handle to the existing namespace immediately. + * Otherwise create a namespace asynchronously. + * + * @param h handle to the file sharing subsystem + * @param name name to use for the namespace + * @return namespace creation context, NULL on error (i.e. invalid filename) + */ +struct GNUNET_FS_NamespaceCreationContext * +GNUNET_FS_namespace_create_start (struct GNUNET_FS_Handle *h, const char *name, + GNUNET_FS_NamespaceCreationCallback cont, void *cont_cls); + + +/** + * Abort namespace creation. + * + * @param ncc namespace creation context to abort + */ +void +GNUNET_FS_namespace_create_stop (struct GNUNET_FS_NamespaceCreationContext *ncc); + + /** * Duplicate a namespace handle. * -- 2.25.1