refactoring datastore API to use MQ API, also fixing misc. bugs in new mysql backend
[oweals/gnunet.git] / src / fs / fs_namespace.c
index a72a64a497cae5d2b3b0a903fa6d97d52134d6e4..69876b8b55a8ae4dbf41afadf06e66f8eeefd636 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet
 /*
      This file is part of GNUnet
-     (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
+     Copyright (C) 2003-2013 GNUnet e.V.
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 2, or (at your
+     by the Free Software Foundation; either version 3, or (at your
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
 
      You should have received a copy of the GNU General Public License
      along with GNUnet; see the file COPYING.  If not, write to the
 
      You should have received a copy of the GNU General Public License
      along with GNUnet; see the file COPYING.  If not, write to the
-     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-     Boston, MA 02111-1307, USA.
+     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+     Boston, MA 02110-1301, USA.
 */
 
 /**
  * @file fs/fs_namespace.c
 */
 
 /**
  * @file fs/fs_namespace.c
- * @brief create and destroy namespaces
+ * @brief publishing to namespaces, and tracking updateable entries
+ *        for our namespaces
  * @author Christian Grothoff
  */
 #include "platform.h"
  * @author Christian Grothoff
  */
 #include "platform.h"
+#include "gnunet_constants.h"
+#include "gnunet_signatures.h"
+#include "gnunet_util_lib.h"
 #include "gnunet_fs_service.h"
 #include "gnunet_fs_service.h"
+#include "fs_api.h"
+#include "fs_publish_ublock.h"
 
 
 /**
 
 
 /**
- * Publish an advertismement for a namespace.  
- *
- * @param h handle to the file sharing subsystem
- * @param namespace handle for the namespace that should be advertised
- * @param meta meta-data for the namespace advertisement
- * @param anonymity for the namespace advertismement
- * @param priority for the namespace advertisement
- * @param expiration for the namespace advertisement
- * @param advertisementURI the keyword (!) URI to advertise the
- *        namespace under (we will create a GNUNET_EC_KNBlock)
- * @param rootEntry name of the root entry in the namespace (for
- *        the namespace advertisement)
- *
- * @return uri of the advertisement
+ * Information about an (updateable) node in the
+ * namespace.
  */
  */
-struct GNUNET_FS_Uri *
-GNUNET_FS_namespace_advertise (struct GNUNET_FS_Handle *h,
-                              struct GNUNET_FS_Namespace *namespace,
-                              const struct GNUNET_CONTAINER_MetaData *meta,
-                              uint32_t anonymity,
-                              uint32_t priority,
-                              struct GNUNET_TIME_Absolute expiration,
-                              const struct GNUNET_FS_Uri *advertisementURI,
-                              const char *rootEntry)
+struct NamespaceUpdateNode
 {
 {
-  GNUNET_break (0);
-  return NULL;
-}
+  /**
+   * Identifier for this node.
+   */
+  char *id;
+
+  /**
+   * Identifier of children of this node.
+   */
+  char *update;
+
+  /**
+   * Metadata for this entry.
+   */
+  struct GNUNET_CONTAINER_MetaData *md;
+
+  /**
+   * URI of this entry in the namespace.
+   */
+  struct GNUNET_FS_Uri *uri;
+
+  /**
+   * Namespace update generation ID.  Used to ensure
+   * freshness of the tree_id.
+   */
+  unsigned int nug;
+
+  /**
+   * TREE this entry belongs to (if nug is current).
+   */
+  unsigned int tree_id;
+
+};
 
 
 /**
 
 
 /**
- * Create a namespace with the given name; if one already
- * exists, return a handle to the existing namespace.
- *
- * @param h handle to the file sharing subsystem
- * @param name name to use for the namespace
- * @return handle to the namespace, NULL on error
+ * Handle to update information for a namespace.
  */
  */
-struct GNUNET_FS_Namespace *
-GNUNET_FS_namespace_create (struct GNUNET_FS_Handle *h,
-                           const char *name)
+struct GNUNET_FS_UpdateInformationGraph
 {
 {
-  GNUNET_break (0);
-  return NULL;
-}
+
+  /**
+   * Handle to the FS service context.
+   */
+  struct GNUNET_FS_Handle *h;
+
+  /**
+   * Array with information about nodes in the namespace.
+   */
+  struct NamespaceUpdateNode **update_nodes;
+
+  /**
+   * Private key for the namespace.
+   */
+  struct GNUNET_CRYPTO_EcdsaPrivateKey ns;
+
+  /**
+   * Hash map mapping identifiers of update nodes
+   * to the update nodes (initialized on-demand).
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *update_map;
+
+  /**
+   * Size of the update nodes array.
+   */
+  unsigned int update_node_count;
+
+  /**
+   * Reference counter.
+   */
+  unsigned int rc;
+
+  /**
+   * Generator for unique nug numbers.
+   */
+  unsigned int nug_gen;
+};
 
 
 /**
 
 
 /**
- * Delete a namespace handle.  Can be used for a clean shutdown (free
- * memory) or also to freeze the namespace to prevent further
- * insertions by anyone.
- *
- * @param namespace handle to the namespace that should be deleted / freed
- * @param freeze prevents future insertions; creating a namespace
- *        with the same name again will create a fresh namespace instead
+ * Return the name of the directory in which we store
+ * the update information graph for the given local namespace.
  *
  *
- * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ * @param h file-sharing handle
+ * @param ns namespace handle
+ * @return NULL on error, otherwise the name of the directory
  */
  */
-int 
-GNUNET_FS_namespace_delete (struct GNUNET_FS_Namespace *namespace,
-                           int freeze)
+static char *
+get_update_information_directory (struct GNUNET_FS_Handle *h,
+                                 const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns)
 {
 {
-  GNUNET_break (0);
-  return GNUNET_SYSERR;
+  char *dn;
+  char *ret;
+  struct GNUNET_CRYPTO_EcdsaPublicKey pub;
+  struct GNUNET_HashCode hc;
+  struct GNUNET_CRYPTO_HashAsciiEncoded enc;
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (h->cfg, "FS", "UPDATE_DIR",
+                                               &dn))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                              "fs", "UPDATE_DIR");
+    return NULL;
+  }
+  GNUNET_CRYPTO_ecdsa_key_get_public (ns, &pub);
+  GNUNET_CRYPTO_hash (&pub, sizeof (pub), &hc);
+  GNUNET_CRYPTO_hash_to_enc (&hc,
+                            &enc);
+  GNUNET_asprintf (&ret, "%s%s%s",
+                  dn,
+                  DIR_SEPARATOR_STR,
+                  (const char *) enc.encoding);
+  GNUNET_free (dn);
+  return ret;
 }
 
 
 /**
 }
 
 
 /**
- * Build a list of all available local (!) namespaces The returned
- * names are only the nicknames since we only iterate over the local
- * namespaces.
+ * Release memory occupied by UIG datastructure.
  *
  *
- * @param h handle to the file sharing subsystem
- * @param cb function to call on each known namespace
- * @param cb_cls closure for cb
+ * @param uig data structure to free
  */
  */
-void 
-GNUNET_FS_namespace_list (struct GNUNET_FS_Handle *h,
-                         GNUNET_FS_NamespaceInfoProcessor cb,
-                         void *cb_cls)
+static void
+free_update_information_graph (struct GNUNET_FS_UpdateInformationGraph *uig)
 {
 {
-  GNUNET_break (0);
+  unsigned int i;
+  struct NamespaceUpdateNode *nsn;
+
+  for (i = 0; i < uig->update_node_count; i++)
+  {
+    nsn = uig->update_nodes[i];
+    GNUNET_CONTAINER_meta_data_destroy (nsn->md);
+    GNUNET_FS_uri_destroy (nsn->uri);
+    GNUNET_free (nsn->id);
+    GNUNET_free (nsn->update);
+    GNUNET_free (nsn);
+  }
+  GNUNET_array_grow (uig->update_nodes, uig->update_node_count,
+                    0);
+  if (NULL != uig->update_map)
+    GNUNET_CONTAINER_multihashmap_destroy (uig->update_map);
+  GNUNET_free (uig);
 }
 
 }
 
-/* end of fs_namespace.c */
-
-#if 0
-/*
-     This file is part of GNUnet
-     (C) 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
-
-     GNUnet is free software; you can redistribute it and/or modify
-     it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 2, or (at your
-     option) any later version.
-
-     GNUnet is distributed in the hope that it will be useful, but
-     WITHOUT ANY WARRANTY; without even the implied warranty of
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-     General Public License for more details.
-
-     You should have received a copy of the GNU General Public License
-     along with GNUnet; see the file COPYING.  If not, write to the
-     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-     Boston, MA 02111-1307, USA.
-*/
 
 /**
 
 /**
- * @file applications/fs/ecrs/namespace.c
- * @brief creation, deletion and advertising of namespaces
- * @author Christian Grothoff
+ * Write a namespace's update node graph to a file.
+ *
+ * @param uig update information graph to dump
  */
  */
-
-#include "platform.h"
-#include "gnunet_directories.h"
-#include "gnunet_protocols.h"
-#include "gnunet_ecrs_lib.h"
-#include "gnunet_fs_lib.h"
-#include "ecrs_core.h"
-#include "ecrs.h"
-
-#define PSEUDODIR "data/namespace/keys/"
-#define INITVALUE "GNUnet!!"
-#define MAX_SBLOCK_SIZE 32000
-
-static char *
-getPseudonymFileName (struct GNUNET_GE_Context *ectx,
-                      struct GNUNET_GC_Configuration *cfg,
-                      const GNUNET_HashCode * pid)
+static void
+write_update_information_graph (struct GNUNET_FS_UpdateInformationGraph *uig)
 {
 {
-  char *gnHome;
-  char *fileName;
-  GNUNET_EncName enc;
-
-  GNUNET_GC_get_configuration_value_filename (cfg,
-                                              "GNUNET",
-                                              "GNUNET_HOME",
-                                              GNUNET_DEFAULT_HOME_DIRECTORY,
-                                              &fileName);
-  gnHome = GNUNET_expand_file_name (ectx, fileName);
-  GNUNET_free (fileName);
-  fileName =
-    GNUNET_malloc (strlen (gnHome) + strlen (PSEUDODIR) +
-                   sizeof (GNUNET_EncName) + 2);
-  strcpy (fileName, gnHome);
-  GNUNET_free (gnHome);
-  strcat (fileName, DIR_SEPARATOR_STR);
-  strcat (fileName, PSEUDODIR);
-  GNUNET_disk_directory_create (ectx, fileName);
-  if (pid != NULL)
+  char *fn;
+  struct GNUNET_BIO_WriteHandle *wh;
+  unsigned int i;
+  struct NamespaceUpdateNode *n;
+  char *uris;
+
+  fn = get_update_information_directory (uig->h,
+                                        &uig->ns);
+  wh = GNUNET_BIO_write_open (fn);
+  if (NULL == wh)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to open `%s' for writing: %s\n"),
+                fn,
+                STRERROR (errno));
+    GNUNET_free (fn);
+    return;
+  }
+  if (GNUNET_OK != GNUNET_BIO_write_int32 (wh, uig->update_node_count))
+    goto END;
+  for (i = 0; i < uig->update_node_count; i++)
+  {
+    n = uig->update_nodes[i];
+    uris = GNUNET_FS_uri_to_string (n->uri);
+    if ((GNUNET_OK != GNUNET_BIO_write_string (wh, n->id)) ||
+        (GNUNET_OK != GNUNET_BIO_write_meta_data (wh, n->md)) ||
+        (GNUNET_OK != GNUNET_BIO_write_string (wh, n->update)) ||
+        (GNUNET_OK != GNUNET_BIO_write_string (wh, uris)))
     {
     {
-      GNUNET_hash_to_enc (pid, &enc);
-      strcat (fileName, (char *) &enc);
+      GNUNET_free (uris);
+      break;
     }
     }
-  return fileName;
+    GNUNET_free (uris);
+  }
+END:
+  if (GNUNET_OK != GNUNET_BIO_write_close (wh))
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to write `%s': %s\n"),
+                fn,
+                STRERROR (errno));
+  GNUNET_free (fn);
 }
 
 
 /**
 }
 
 
 /**
- * Check if the given namespace exists (locally).
+ * Read the namespace update node graph from a file.
  *
  *
- * @return GNUNET_OK if the namespace exists, GNUNET_SYSERR if not
+ * @param h FS handle to use
+ * @param ns namespace to read
+ * @return update graph, never NULL
  */
  */
-int
-GNUNET_ECRS_namespace_test_exists (struct GNUNET_GE_Context *ectx,
-                                   struct GNUNET_GC_Configuration *cfg,
-                                   const GNUNET_HashCode * pid)
+static struct GNUNET_FS_UpdateInformationGraph *
+read_update_information_graph (struct GNUNET_FS_Handle *h,
+                              const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns)
 {
 {
-  char *fileName;
-  int ret;
-
-  fileName = getPseudonymFileName (ectx, cfg, pid);
-  ret = GNUNET_disk_file_test (ectx, fileName);
-  GNUNET_free (fileName);
-  return ret;
+  struct GNUNET_FS_UpdateInformationGraph *uig;
+  char *fn;
+  struct GNUNET_BIO_ReadHandle *rh;
+  unsigned int i;
+  struct NamespaceUpdateNode *n;
+  char *uris;
+  uint32_t count;
+  char *emsg;
+
+  uig = GNUNET_new (struct GNUNET_FS_UpdateInformationGraph);
+  uig->h = h;
+  uig->ns = *ns;
+  fn = get_update_information_directory (h, ns);
+  if (GNUNET_YES != GNUNET_DISK_file_test (fn))
+  {
+    GNUNET_free (fn);
+    return uig;
+  }
+  rh = GNUNET_BIO_read_open (fn);
+  if (NULL == rh)
+  {
+    GNUNET_free (fn);
+    return uig;
+  }
+  if (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &count))
+  {
+    GNUNET_break (0);
+    goto END;
+  }
+  if (count > 1024 * 1024)
+  {
+    GNUNET_break (0);
+    goto END;
+  }
+  if (0 == count)
+    goto END;
+  uig->update_nodes =
+    GNUNET_malloc (count * sizeof (struct NamespaceUpdateNode *));
+
+  for (i = 0; i < count; i++)
+  {
+    n = GNUNET_new (struct NamespaceUpdateNode);
+    if ((GNUNET_OK != GNUNET_BIO_read_string (rh, "identifier", &n->id, 1024))
+        || (GNUNET_OK != GNUNET_BIO_read_meta_data (rh, "meta", &n->md)) ||
+        (GNUNET_OK !=
+         GNUNET_BIO_read_string (rh, "update-id", &n->update, 1024)) ||
+        (GNUNET_OK != GNUNET_BIO_read_string (rh, "uri", &uris, 1024 * 2)))
+    {
+      GNUNET_break (0);
+      GNUNET_free_non_null (n->id);
+      GNUNET_free_non_null (n->update);
+      if (n->md != NULL)
+        GNUNET_CONTAINER_meta_data_destroy (n->md);
+      GNUNET_free (n);
+      break;
+    }
+    n->uri = GNUNET_FS_uri_parse (uris, &emsg);
+    GNUNET_free (uris);
+    if (n->uri == NULL)
+    {
+      GNUNET_break (0);
+      GNUNET_free (emsg);
+      GNUNET_free (n->id);
+      GNUNET_free_non_null (n->update);
+      GNUNET_CONTAINER_meta_data_destroy (n->md);
+      GNUNET_free (n);
+      break;
+    }
+    uig->update_nodes[i] = n;
+  }
+  uig->update_node_count = i;
+ END:
+  if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to read `%s': %s\n"),
+               fn, emsg);
+    GNUNET_free (emsg);
+  }
+  GNUNET_free (fn);
+  return uig;
 }
 
 }
 
+
 /**
 /**
- * Delete a local namespace.
- *
- * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ * Context for the SKS publication.
  */
  */
-int
-GNUNET_ECRS_namespace_delete (struct GNUNET_GE_Context *ectx,
-                              struct GNUNET_GC_Configuration *cfg,
-                              const GNUNET_HashCode * pid)
+struct GNUNET_FS_PublishSksContext
 {
 {
-  char *fileName;
 
 
-  fileName = getPseudonymFileName (ectx, cfg, pid);
-  if (GNUNET_YES != GNUNET_disk_file_test (ectx, fileName))
-    {
-      GNUNET_free (fileName);
-      return GNUNET_SYSERR;     /* no such namespace */
-    }
-  if (0 != UNLINK (fileName))
-    {
-      GNUNET_GE_LOG_STRERROR_FILE (ectx,
-                                   GNUNET_GE_WARNING | GNUNET_GE_USER |
-                                   GNUNET_GE_BULK, "unlink", fileName);
-      GNUNET_free (fileName);
-      return GNUNET_SYSERR;
-    }
-  GNUNET_free (fileName);
-  return GNUNET_OK;
-}
+  /**
+   * URI of the new entry in the namespace.
+   */
+  struct GNUNET_FS_Uri *uri;
+
+  /**
+   * Namespace update node to add to namespace on success (or to be
+   * deleted if publishing failed).
+   */
+  struct NamespaceUpdateNode *nsn;
+
+  /**
+   * Namespace we're publishing to.
+   */
+  struct GNUNET_CRYPTO_EcdsaPrivateKey ns;
+
+  /**
+   * Handle to the datastore.
+   */
+  struct GNUNET_DATASTORE_Handle *dsh;
+
+  /**
+   * Handle to FS.
+   */
+  struct GNUNET_FS_Handle *h;
+
+  /**
+   * Function to call once we're done.
+   */
+  GNUNET_FS_PublishContinuation cont;
+
+  /**
+   * Closure for cont.
+   */
+  void *cont_cls;
+
+  /**
+   * Handle for our UBlock operation request.
+   */
+  struct GNUNET_FS_PublishUblockContext *uc;
+};
+
 
 /**
 
 /**
- * Write the private key of the namespace to a file.
+ * Function called by the UBlock construction with
+ * the result from the PUT (UBlock) request.
+ *
+ * @param cls closure of type "struct GNUNET_FS_PublishSksContext*"
+ * @param msg error message (or NULL)
  */
  */
-static int
-write_namespace_key (struct GNUNET_GC_Configuration *cfg,
-                     const struct GNUNET_RSA_PrivateKey *key)
+static void
+sks_publish_cont (void *cls,
+                 const char *msg)
 {
 {
-  GNUNET_RSA_PrivateKeyEncoded *namespace_priv_key_encoded;
-  char *fileName;
-  GNUNET_RSA_PublicKey pubk;
-  GNUNET_HashCode pid;
-
-  GNUNET_RSA_get_public_key (key, &pubk);
-  GNUNET_hash (&pubk, sizeof (GNUNET_RSA_PublicKey), &pid);
-  fileName = getPseudonymFileName (NULL, cfg, &pid);
-  if (GNUNET_YES == GNUNET_disk_file_test (NULL, fileName))
-    {
-      GNUNET_GE_BREAK (NULL, 0);        /* hash collision!? */
-      GNUNET_free (fileName);
-      return GNUNET_SYSERR;
-    }
-  namespace_priv_key_encoded = GNUNET_RSA_encode_key (key);
-  GNUNET_disk_file_write (NULL, fileName,
-                          (const char *) namespace_priv_key_encoded,
-                          ntohs (namespace_priv_key_encoded->len), "600");
-  GNUNET_free (fileName);
-  GNUNET_free (namespace_priv_key_encoded);
-  return GNUNET_OK;
+  struct GNUNET_FS_PublishSksContext *psc = cls;
+  struct GNUNET_FS_UpdateInformationGraph *uig;
+
+  psc->uc = NULL;
+  if (NULL != msg)
+  {
+    if (NULL != psc->cont)
+      psc->cont (psc->cont_cls, NULL, msg);
+    GNUNET_FS_publish_sks_cancel (psc);
+    return;
+  }
+  if (NULL != psc->nsn)
+  {
+    /* FIXME: this can be done much more
+     * efficiently by simply appending to the
+     * file and overwriting the 4-byte header */
+    uig = read_update_information_graph (psc->h,
+                                        &psc->ns);
+    GNUNET_array_append (uig->update_nodes,
+                        uig->update_node_count,
+                        psc->nsn);
+    psc->nsn = NULL;
+    write_update_information_graph (uig);
+    free_update_information_graph (uig);
+  }
+  if (NULL != psc->cont)
+    psc->cont (psc->cont_cls, psc->uri, NULL);
+  GNUNET_FS_publish_sks_cancel (psc);
 }
 
 }
 
+
 /**
 /**
- * Create a new namespace (and publish an advertismement).
- * This publishes both an GNUNET_EC_NBlock in the namespace itself
- * as well as KNBlocks under all keywords specified in
- * the advertisementURI.
- *
- * @param anonymity_level for the namespace advertismement
- * @param priority for the namespace advertisement
- * @param expiration for the namespace advertisement
- * @param advertisementURI the keyword (!) URI to advertise the
- *        namespace under (GNUNET_EC_KNBlock)
- * @param meta meta-data for the namespace advertisement
- *        (will be used to derive a name)
- * @param rootEntry name of the root entry in the namespace (for
- *        the namespace advertisement)
- * @param rootURI set to the URI of the namespace, NULL if
- *        no advertisement was created
+ * Publish an SBlock on GNUnet.
  *
  *
- * @return URI on success, NULL on error
+ * @param h handle to the file sharing subsystem
+ * @param ns namespace to publish in
+ * @param identifier identifier to use
+ * @param update update identifier to use
+ * @param meta metadata to use
+ * @param uri URI to refer to in the SBlock
+ * @param bo block options
+ * @param options publication options
+ * @param cont continuation
+ * @param cont_cls closure for cont
+ * @return NULL on error ('cont' will still be called)
  */
  */
-struct GNUNET_ECRS_URI *
-GNUNET_ECRS_namespace_create (struct GNUNET_GE_Context *ectx,
-                              struct GNUNET_GC_Configuration *cfg,
-                              const struct GNUNET_CONTAINER_MetaData *meta,
-                              uint32_t anonymityLevel,
-                              uint32_t priority,
-                              GNUNET_CronTime expiration,
-                              const struct GNUNET_ECRS_URI *advertisementURI,
-                              const char *rootEntry)
+struct GNUNET_FS_PublishSksContext *
+GNUNET_FS_publish_sks (struct GNUNET_FS_Handle *h,
+                       const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns,
+                       const char *identifier, const char *update,
+                       const struct GNUNET_CONTAINER_MetaData *meta,
+                       const struct GNUNET_FS_Uri *uri,
+                       const struct GNUNET_FS_BlockOptions *bo,
+                       enum GNUNET_FS_PublishOptions options,
+                       GNUNET_FS_PublishContinuation cont, void *cont_cls)
 {
 {
-  struct GNUNET_ECRS_URI *rootURI;
-  struct GNUNET_RSA_PrivateKey *namespace_priv_key;
-  GNUNET_HashCode hc;
-  struct GNUNET_ClientServerConnection *sock;
-  GNUNET_DatastoreValue *value;
-  GNUNET_DatastoreValue *knvalue;
-  unsigned int size;
-  unsigned int mdsize;
-  struct GNUNET_RSA_PrivateKey *pk;
-  GNUNET_EC_SBlock *sb;
-  GNUNET_EC_KSBlock *ksb;
-  char **keywords;
-  const char *keyword;
-  unsigned int keywordCount;
-  int i;
-  char *cpy;
-  char *rtgt;
-
-  if ((advertisementURI != NULL)
-      && (!GNUNET_ECRS_uri_test_ksk (advertisementURI)))
+  struct GNUNET_FS_PublishSksContext *psc;
+  struct GNUNET_FS_Uri *sks_uri;
+
+  sks_uri = GNUNET_new (struct GNUNET_FS_Uri);
+  sks_uri->type = GNUNET_FS_URI_SKS;
+  sks_uri->data.sks.identifier = GNUNET_strdup (identifier);
+  GNUNET_CRYPTO_ecdsa_key_get_public (ns,
+                                   &sks_uri->data.sks.ns);
+
+  psc = GNUNET_new (struct GNUNET_FS_PublishSksContext);
+  psc->h = h;
+  psc->uri = sks_uri;
+  psc->cont = cont;
+  psc->cont_cls = cont_cls;
+  psc->ns = *ns;
+  if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
+  {
+    psc->dsh = GNUNET_DATASTORE_connect (h->cfg);
+    if (NULL == psc->dsh)
     {
     {
-      GNUNET_GE_BREAK (ectx, 0);
+      sks_publish_cont (psc,
+                       _("Failed to connect to datastore."));
       return NULL;
     }
       return NULL;
     }
-  namespace_priv_key = GNUNET_RSA_create_key ();
-  if (GNUNET_OK != write_namespace_key (cfg, namespace_priv_key))
-    {
-      GNUNET_RSA_free_key (namespace_priv_key);
-      return NULL;
-    }
-
-  /* create advertisements */
-  mdsize = GNUNET_meta_data_get_serialized_size (meta, GNUNET_SERIALIZE_PART);
-  size = mdsize + sizeof (GNUNET_EC_SBlock) + strlen (rootEntry) + 2;
-  if (size > MAX_SBLOCK_SIZE)
-    {
-      size = MAX_SBLOCK_SIZE;
-      mdsize = size - sizeof (GNUNET_EC_SBlock) - strlen (rootEntry) - 2;
-    }
-  value = GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + size);
-  memset (value, 0, sizeof (GNUNET_DatastoreValue) + size);
-  sb = (GNUNET_EC_SBlock *) & value[1];
-  sb->type = htonl (GNUNET_ECRS_BLOCKTYPE_SIGNED);
-  GNUNET_RSA_get_public_key (namespace_priv_key, &sb->subspace);
-  rtgt = (char *) &sb[1];
-  memcpy (rtgt, rootEntry, strlen (rootEntry) + 1);
-  mdsize = GNUNET_meta_data_serialize (ectx,
-                                       meta,
-                                       &rtgt[strlen (rootEntry) + 2],
-                                       mdsize, GNUNET_SERIALIZE_PART);
-  if (mdsize == -1)
-    {
-      GNUNET_GE_BREAK (ectx, 0);
-      GNUNET_RSA_free_key (namespace_priv_key);
-      GNUNET_free (value);
-      return NULL;
-    }
-  size = mdsize + sizeof (GNUNET_EC_SBlock) + strlen (rootEntry) + 2;
-  GNUNET_GE_ASSERT (ectx,
-                    GNUNET_OK == GNUNET_RSA_sign (namespace_priv_key,
-                                                  size
-                                                  -
-                                                  sizeof
-                                                  (GNUNET_RSA_Signature) -
-                                                  sizeof
-                                                  (GNUNET_RSA_PublicKey) -
-                                                  sizeof (unsigned int),
-                                                  &sb->identifier,
-                                                  &sb->signature));
-  value->size = htonl (sizeof (GNUNET_DatastoreValue) + size);
-  value->type = htonl (GNUNET_ECRS_BLOCKTYPE_SIGNED);
-  value->priority = htonl (priority);
-  value->anonymity_level = htonl (anonymityLevel);
-  value->expiration_time = GNUNET_htonll (expiration);
-  sock = GNUNET_client_connection_create (ectx, cfg);
-  if (sock == NULL)
-    {
-      GNUNET_free (value);
-      GNUNET_RSA_free_key (namespace_priv_key);
-      return NULL;
-    }
-  if (GNUNET_OK != GNUNET_FS_insert (sock, value))
-    {
-      GNUNET_free (value);
-      GNUNET_client_connection_destroy (sock);
-      GNUNET_RSA_free_key (namespace_priv_key);
-      return NULL;
-    }
-
-
-  /* publish KNBlocks */
-  size += sizeof (GNUNET_EC_KSBlock) - sizeof (GNUNET_EC_SBlock);
-  knvalue = GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + size);
-  *knvalue = *value;
-  knvalue->type = htonl (GNUNET_ECRS_BLOCKTYPE_KEYWORD_SIGNED);
-  knvalue->size = htonl (sizeof (GNUNET_DatastoreValue) + size);
-  ksb = (GNUNET_EC_KSBlock *) & knvalue[1];
-  ksb->type = htonl (GNUNET_ECRS_BLOCKTYPE_KEYWORD_SIGNED);
-  memcpy (&ksb->sblock,
-          sb, sizeof (GNUNET_EC_SBlock) + mdsize + strlen (rootEntry) + 2);
-
-  if (advertisementURI != NULL)
-    {
-      keywords = advertisementURI->data.ksk.keywords;
-      keywordCount = advertisementURI->data.ksk.keywordCount;
-      cpy =
-        GNUNET_malloc (size - sizeof (GNUNET_EC_KBlock) -
-                       sizeof (unsigned int));
-      memcpy (cpy,
-              &ksb->sblock,
-              size - sizeof (GNUNET_EC_KBlock) - sizeof (unsigned int));
-      for (i = 0; i < keywordCount; i++)
-        {
-          keyword = keywords[i];
-          /* first character of keyword indicates
-             mandatory or not -- ignore for hashing! */
-          GNUNET_hash (&keyword[1], strlen (&keyword[1]), &hc);
-          pk = GNUNET_RSA_create_key_from_hash (&hc);
-          GNUNET_RSA_get_public_key (pk, &ksb->kblock.keyspace);
-          GNUNET_GE_ASSERT (ectx,
-                            size - sizeof (GNUNET_EC_KBlock) -
-                            sizeof (unsigned int) ==
-                            sizeof (GNUNET_EC_SBlock) + mdsize +
-                            strlen (rootEntry) + 2);
-          GNUNET_ECRS_encryptInPlace (&hc, &ksb->sblock,
-                                      size - sizeof (GNUNET_EC_KBlock) -
-                                      sizeof (unsigned int));
-
-          GNUNET_GE_ASSERT (ectx,
-                            GNUNET_OK == GNUNET_RSA_sign (pk,
-                                                          size -
-                                                          sizeof
-                                                          (GNUNET_EC_KBlock) -
-                                                          sizeof (unsigned
-                                                                  int),
-                                                          &ksb->sblock,
-                                                          &ksb->
-                                                          kblock.signature));
-          /* extra check: verify sig */
-          GNUNET_RSA_free_key (pk);
-          if (GNUNET_OK != GNUNET_FS_insert (sock, knvalue))
-            {
-              GNUNET_GE_BREAK (ectx, 0);
-              GNUNET_free (cpy);
-              GNUNET_free (knvalue);
-              GNUNET_free (value);
-              GNUNET_client_connection_destroy (sock);
-              GNUNET_RSA_free_key (namespace_priv_key);
-              return NULL;
-            }
-          /* restore nblock to avoid re-encryption! */
-          memcpy (&ksb->sblock,
-                  cpy,
-                  size - sizeof (GNUNET_EC_KBlock) - sizeof (unsigned int));
-        }
-      GNUNET_free (cpy);
-    }
-  rootURI = GNUNET_malloc (sizeof (URI));
-  rootURI->type = sks;
-  GNUNET_hash (&sb->subspace,
-               sizeof (GNUNET_RSA_PublicKey), &rootURI->data.sks.namespace);
-  rootURI->data.sks.identifier = GNUNET_strdup (rootEntry);
-  GNUNET_free (knvalue);
-  GNUNET_free (value);
-  GNUNET_client_connection_destroy (sock);
-  GNUNET_RSA_free_key (namespace_priv_key);
-
-  return rootURI;
+  }
+  if (NULL != update)
+  {
+    psc->nsn = GNUNET_new (struct NamespaceUpdateNode);
+    psc->nsn->id = GNUNET_strdup (identifier);
+    psc->nsn->update = GNUNET_strdup (update);
+    psc->nsn->md = GNUNET_CONTAINER_meta_data_duplicate (meta);
+    psc->nsn->uri = GNUNET_FS_uri_dup (uri);
+  }
+  psc->uc = GNUNET_FS_publish_ublock_ (h,
+                                      psc->dsh,
+                                      identifier,
+                                      update,
+                                      ns,
+                                      meta,
+                                      uri,
+                                      bo,
+                                      options,
+                                      &sks_publish_cont,
+                                      psc);
+  return psc;
 }
 
 }
 
-static struct GNUNET_RSA_PrivateKey *
-read_namespace_key (struct GNUNET_GC_Configuration *cfg,
-                    const GNUNET_HashCode * pid)
+
+/**
+ * Abort the SKS publishing operation.
+ *
+ * @param psc context of the operation to abort.
+ */
+void
+GNUNET_FS_publish_sks_cancel (struct GNUNET_FS_PublishSksContext *psc)
 {
 {
-  char *fileName;
-  GNUNET_RSA_PrivateKeyEncoded *hke;
-  struct GNUNET_RSA_PrivateKey *hk;
-  char *dst;
-  unsigned long long len;
-
-  fileName = getPseudonymFileName (NULL, cfg, pid);
-  if (GNUNET_OK != GNUNET_disk_file_size (NULL, fileName, &len, GNUNET_YES))
-    {
-      GNUNET_free (fileName);
-      return NULL;
-    }
-  if (len < 2)
-    {
-      GNUNET_GE_LOG (NULL, GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
-                     _("File `%s' does not contain a pseudonym.\n"),
-                     fileName);
-      GNUNET_free (fileName);
-      return NULL;
-    }
-  dst = GNUNET_malloc (len);
-  len = GNUNET_disk_file_read (NULL, fileName, len, dst);
-  hke = (GNUNET_RSA_PrivateKeyEncoded *) dst;
-  if (ntohs (hke->len) != len)
-    {
-      GNUNET_GE_LOG (NULL, GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
-                     _("Format of pseudonym `%s' is invalid.\n"), fileName);
-      GNUNET_free (fileName);
-      GNUNET_free (hke);
-      return NULL;
-    }
-  GNUNET_free (fileName);
-  hk = GNUNET_RSA_decode_key (hke);
-  GNUNET_free (hke);
-  return hk;
+  if (NULL != psc->uc)
+  {
+    GNUNET_FS_publish_ublock_cancel_ (psc->uc);
+    psc->uc = NULL;
+  }
+  if (NULL != psc->dsh)
+  {
+    GNUNET_DATASTORE_disconnect (psc->dsh, GNUNET_NO);
+    psc->dsh = NULL;
+  }
+  GNUNET_FS_uri_destroy (psc->uri);
+  if (NULL != psc->nsn)
+  {
+    GNUNET_CONTAINER_meta_data_destroy (psc->nsn->md);
+    GNUNET_FS_uri_destroy (psc->nsn->uri);
+    GNUNET_free (psc->nsn->id);
+    GNUNET_free (psc->nsn->update);
+    GNUNET_free (psc->nsn);
+  }
+  GNUNET_free (psc);
 }
 
 
 /**
 }
 
 
 /**
- * Add an entry into a namespace.
- *
- * @param dstU to which URI should the namespace entry refer?
- * @param md what meta-data should be associated with the
- *        entry?
- * @param thisId name of this entry in the namespace (keyword/identifier)
- * @param nextId name of the update for this entry (to be published in
- *               the future; maybe NULL)
- * @param pid unique identifier of the namespace/pseudonym
- * @return URI on success, NULL on error
+ * Closure for 'process_update_node'.
  */
  */
-struct GNUNET_ECRS_URI *
-GNUNET_ECRS_namespace_add_content (struct GNUNET_GE_Context *ectx,
-                                   struct GNUNET_GC_Configuration *cfg,
-                                   const GNUNET_HashCode * pid,
-                                   uint32_t anonymityLevel,
-                                   uint32_t priority,
-                                   GNUNET_CronTime expiration,
-                                   const char *thisId,
-                                   const char *nextId,
-                                   const struct GNUNET_ECRS_URI *dstU,
-                                   const struct GNUNET_MetaData *md)
+struct ProcessUpdateClosure
 {
 {
-  struct GNUNET_ECRS_URI *uri;
-  struct GNUNET_ClientServerConnection *sock;
-  GNUNET_DatastoreValue *value;
-  unsigned int size;
-  unsigned int mdsize;
-  struct GNUNET_RSA_PrivateKey *hk;
-  GNUNET_EC_SBlock *sb;
-  char *dstURI;
-  char *destPos;
-  GNUNET_HashCode hc;           /* hash of thisId = key */
-  GNUNET_HashCode hc2;          /* hash of hc = identifier */
-  int ret;
-  unsigned int nidlen;
-
-  hk = read_namespace_key (cfg, pid);
-  if (hk == NULL)
-    return NULL;
+  /**
+   * Function to call for each node.
+   */
+  GNUNET_FS_IdentifierProcessor ip;
+
+  /**
+   * Closure for 'ip'.
+   */
+  void *ip_cls;
+};
 
 
-  /* THEN: construct GNUNET_EC_SBlock */
-  dstURI = GNUNET_ECRS_uri_to_string (dstU);
-  mdsize = GNUNET_meta_data_get_serialized_size (md, GNUNET_SERIALIZE_PART);
-  if (nextId == NULL)
-    nextId = "";
-  nidlen = strlen (nextId) + 1;
-  size = mdsize + sizeof (GNUNET_EC_SBlock) + strlen (dstURI) + 1 + nidlen;
-  if (size > MAX_SBLOCK_SIZE)
-    {
-      size = MAX_SBLOCK_SIZE;
-      mdsize =
-        size - (sizeof (GNUNET_EC_SBlock) + strlen (dstURI) + 1 + nidlen);
-    }
-  value = GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + size);
-  sb = (GNUNET_EC_SBlock *) & value[1];
-  sb->type = htonl (GNUNET_ECRS_BLOCKTYPE_SIGNED);
-  destPos = (char *) &sb[1];
-  memcpy (destPos, nextId, nidlen);
-  destPos += nidlen;
-  memcpy (destPos, dstURI, strlen (dstURI) + 1);
-  destPos += strlen (dstURI) + 1;
-  mdsize = GNUNET_meta_data_serialize (ectx,
-                                       md,
-                                       destPos,
-                                       mdsize, GNUNET_SERIALIZE_PART);
-  if (mdsize == -1)
-    {
-      GNUNET_GE_BREAK (ectx, 0);
-      GNUNET_free (dstURI);
-      GNUNET_RSA_free_key (hk);
-      GNUNET_free (value);
-      return NULL;
-    }
-  size = sizeof (GNUNET_EC_SBlock) + mdsize + strlen (dstURI) + 1 + nidlen;
-  value->size = htonl (sizeof (GNUNET_DatastoreValue) + size);
-  value->type = htonl (GNUNET_ECRS_BLOCKTYPE_SIGNED);
-  value->priority = htonl (priority);
-  value->anonymity_level = htonl (anonymityLevel);
-  value->expiration_time = GNUNET_htonll (expiration);
-  GNUNET_hash (thisId, strlen (thisId), &hc);
-  GNUNET_hash (&hc, sizeof (GNUNET_HashCode), &hc2);
-  uri = GNUNET_malloc (sizeof (URI));
-  uri->type = sks;
-  GNUNET_RSA_get_public_key (hk, &sb->subspace);
-  GNUNET_hash (&sb->subspace,
-               sizeof (GNUNET_RSA_PublicKey), &uri->data.sks.namespace);
-  GNUNET_GE_BREAK (ectx, 0 == memcmp (&uri->data.sks.namespace,
-                                      pid, sizeof (GNUNET_HashCode)));
-  uri->data.sks.identifier = GNUNET_strdup (thisId);
-  GNUNET_hash_xor (&hc2, &uri->data.sks.namespace, &sb->identifier);
-  GNUNET_ECRS_encryptInPlace (&hc, &sb[1], size - sizeof (GNUNET_EC_SBlock));
-  GNUNET_GE_ASSERT (ectx,
-                    GNUNET_OK == GNUNET_RSA_sign (hk,
-                                                  size
-                                                  -
-                                                  sizeof
-                                                  (GNUNET_RSA_Signature) -
-                                                  sizeof
-                                                  (GNUNET_RSA_PublicKey) -
-                                                  sizeof (unsigned int),
-                                                  &sb->identifier,
-                                                  &sb->signature));
-  GNUNET_RSA_free_key (hk);
-  sock = GNUNET_client_connection_create (ectx, cfg);
-  ret = GNUNET_FS_insert (sock, value);
-  if (ret != GNUNET_OK)
-    {
-      GNUNET_free (uri);
-      uri = NULL;
-    }
-  GNUNET_client_connection_destroy (sock);
-  GNUNET_free (value);
-  GNUNET_free (dstURI);
 
 
-  return uri;
+/**
+ * Call the iterator in the closure for each node.
+ *
+ * @param cls closure (of type 'struct ProcessUpdateClosure *')
+ * @param key current key code
+ * @param value value in the hash map (of type 'struct NamespaceUpdateNode *')
+ * @return GNUNET_YES if we should continue to
+ *         iterate,
+ *         GNUNET_NO if not.
+ */
+static int
+process_update_node (void *cls,
+                    const struct GNUNET_HashCode *key,
+                    void *value)
+{
+  struct ProcessUpdateClosure *pc = cls;
+  struct NamespaceUpdateNode *nsn = value;
+
+  pc->ip (pc->ip_cls,
+         nsn->id,
+         nsn->uri,
+         nsn->md,
+         nsn->update);
+  return GNUNET_YES;
 }
 
 }
 
-struct lNCLS
+
+/**
+ * Closure for 'find_trees'.
+ */
+struct FindTreeClosure
 {
 {
-  struct GNUNET_GE_Context *ectx;
-  struct GNUNET_GC_Configuration *cfg;
-  GNUNET_ECRS_NamespaceInfoProcessor cb;
-  void *cls;
-  int cnt;
+  /**
+   * UIG we are operating on.
+   */
+  struct GNUNET_FS_UpdateInformationGraph *uig;
+
+  /**
+   * Array with 'head's of TREEs.
+   */
+  struct NamespaceUpdateNode **tree_array;
+
+  /**
+   * Size of 'tree_array'
+   */
+  unsigned int tree_array_size;
+
+  /**
+   * Current generational ID used.
+   */
+  unsigned int nug;
+
+  /**
+   * Identifier for the current TREE, or UINT_MAX for none yet.
+   */
+  unsigned int id;
 };
 
 };
 
+
+/**
+ * Find all nodes reachable from the current node (including the
+ * current node itself).  If they are in no tree, add them to the
+ * current one.   If they are the head of another tree, merge the
+ * trees.  If they are in the middle of another tree, let them be.
+ * We can tell that a node is already in an tree by checking if
+ * its 'nug' field is set to the current 'nug' value.  It is the
+ * head of an tree if it is in the 'tree_array' under its respective
+ * 'tree_id'.
+ *
+ * In short, we're trying to find the smallest number of tree to
+ * cover a directed graph.
+ *
+ * @param cls closure (of type 'struct FindTreeClosure')
+ * @param key current key code
+ * @param value value in the hash map
+ * @return GNUNET_YES if we should continue to
+ *         iterate,
+ *         GNUNET_NO if not.
+ */
 static int
 static int
-processFile_ (void *cls, const char *fileName)
+find_trees (void *cls,
+           const struct GNUNET_HashCode *key,
+           void *value)
 {
 {
-  struct lNCLS *c = cls;
-  struct GNUNET_RSA_PrivateKey *hk;
-  GNUNET_RSA_PrivateKeyEncoded *hke;
-  char *dst;
-  unsigned long long len;
-  GNUNET_HashCode namespace;
-  GNUNET_RSA_PublicKey pk;
-  const char *name;
+  struct FindTreeClosure *fc = cls;
+  struct NamespaceUpdateNode *nsn = value;
+  struct GNUNET_HashCode hc;
+
+  if (nsn->nug == fc->nug)
+  {
+    if (UINT_MAX == nsn->tree_id)
+      return GNUNET_YES;        /* circular */
+    GNUNET_assert (nsn->tree_id < fc->tree_array_size);
+    if (fc->tree_array[nsn->tree_id] != nsn)
+      return GNUNET_YES;        /* part of "another" (directed) TREE,
+                                 * and not root of it, end trace */
+    if (nsn->tree_id == fc->id)
+      return GNUNET_YES;        /* that's our own root (can this be?) */
+    /* merge existing TREE, we have a root for both */
+    fc->tree_array[nsn->tree_id] = NULL;
+    if (UINT_MAX == fc->id)
+      fc->id = nsn->tree_id;    /* take over ID */
+  }
+  else
+  {
+    nsn->nug = fc->nug;
+    nsn->tree_id = UINT_MAX;    /* mark as undef */
+    /* trace */
+    GNUNET_CRYPTO_hash (nsn->update, strlen (nsn->update), &hc);
+    GNUNET_CONTAINER_multihashmap_get_multiple (fc->uig->update_map, &hc,
+                                                &find_trees, fc);
+  }
+  return GNUNET_YES;
+}
 
 
-  if (GNUNET_OK !=
-      GNUNET_disk_file_size (c->ectx, fileName, &len, GNUNET_YES))
-    return GNUNET_OK;
-  if (len < 2)
+
+/**
+ * List all of the identifiers in the namespace for which we could
+ * produce an update.  Namespace updates form a graph where each node
+ * has a name.  Each node can have any number of URI/meta-data entries
+ * which can each be linked to other nodes.  Cycles are possible.
+ *
+ * Calling this function with "next_id" NULL will cause the library to
+ * call "ip" with a root for each strongly connected component of the
+ * graph (a root being a node from which all other nodes in the Tree
+ * are reachable).
+ *
+ * Calling this function with "next_id" being the name of a node will
+ * cause the library to call "ip" with all children of the node.  Note
+ * that cycles within the final tree are possible (including self-loops).
+ * I know, odd definition of a tree, but the GUI will display an actual
+ * tree (GtkTreeView), so that's what counts for the term here.
+ *
+ * @param h fs handle to use
+ * @param ns namespace to inspect for updateable content
+ * @param next_id ID to look for; use NULL to look for tree roots
+ * @param ip function to call on each updateable identifier
+ * @param ip_cls closure for ip
+ */
+void
+GNUNET_FS_namespace_list_updateable (struct GNUNET_FS_Handle *h,
+                                    const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns,
+                                     const char *next_id,
+                                     GNUNET_FS_IdentifierProcessor ip,
+                                     void *ip_cls)
+{
+  unsigned int i;
+  unsigned int nug;
+  struct GNUNET_HashCode hc;
+  struct NamespaceUpdateNode *nsn;
+  struct ProcessUpdateClosure pc;
+  struct FindTreeClosure fc;
+  struct GNUNET_FS_UpdateInformationGraph *uig;
+
+  uig = read_update_information_graph (h, ns);
+  if (NULL == uig->update_nodes)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "No updateable nodes found for ID `%s'\n", next_id);
+    free_update_information_graph (uig);
+    return;                     /* no nodes */
+  }
+  uig->update_map =
+    GNUNET_CONTAINER_multihashmap_create (2 +
+                                         3 * uig->update_node_count /
+                                         4,
+                                         GNUNET_NO);
+  for (i = 0; i < uig->update_node_count; i++)
+  {
+    nsn = uig->update_nodes[i];
+    GNUNET_CRYPTO_hash (nsn->id, strlen (nsn->id), &hc);
+    GNUNET_CONTAINER_multihashmap_put (uig->update_map, &hc, nsn,
+                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+  }
+  if (NULL != next_id)
+  {
+    GNUNET_CRYPTO_hash (next_id, strlen (next_id), &hc);
+    pc.ip = ip;
+    pc.ip_cls = ip_cls;
+    GNUNET_CONTAINER_multihashmap_get_multiple (uig->update_map, &hc,
+                                                &process_update_node, &pc);
+    free_update_information_graph (uig);
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Calculating TREEs to find roots of update trees\n");
+  /* Find heads of TREEs in update graph */
+  nug = ++uig->nug_gen;
+  fc.tree_array = NULL;
+  fc.tree_array_size = 0;
+
+  for (i = 0; i < uig->update_node_count; i++)
+  {
+    nsn = uig->update_nodes[i];
+    if (nsn->nug == nug)
     {
     {
-      GNUNET_GE_LOG (c->ectx,
-                     GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
-                     _("Format of file `%s' is invalid, trying to remove.\n"),
-                     fileName);
-      UNLINK (fileName);
-      return GNUNET_OK;
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TREE of node `%s' is %u\n", nsn->id,
+                  nsn->nug);
+      continue;                 /* already placed in TREE */
     }
     }
-  dst = GNUNET_malloc (len);
-  len = GNUNET_disk_file_read (c->ectx, fileName, len, dst);
-  hke = (GNUNET_RSA_PrivateKeyEncoded *) dst;
-  if (ntohs (hke->len) != len)
+    GNUNET_CRYPTO_hash (nsn->update, strlen (nsn->update), &hc);
+    nsn->nug = nug;
+    nsn->tree_id = UINT_MAX;
+    fc.id = UINT_MAX;
+    fc.nug = nug;
+    fc.uig = uig;
+    GNUNET_CONTAINER_multihashmap_get_multiple (uig->update_map, &hc,
+                                                &find_trees, &fc);
+    if (UINT_MAX == fc.id)
     {
     {
-      GNUNET_GE_LOG (c->ectx,
-                     GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
-                     _("Format of file `%s' is invalid, trying to remove.\n"),
-                     fileName);
-      UNLINK (fileName);
-      GNUNET_free (hke);
-      return GNUNET_OK;
+      /* start new TREE */
+      for (fc.id = 0; fc.id < fc.tree_array_size; fc.id++)
+      {
+        if (NULL == fc.tree_array[fc.id])
+        {
+          fc.tree_array[fc.id] = nsn;
+          nsn->tree_id = fc.id;
+          break;
+        }
+      }
+      if (fc.id == fc.tree_array_size)
+      {
+        GNUNET_array_append (fc.tree_array, fc.tree_array_size, nsn);
+        nsn->tree_id = fc.id;
+      }
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Starting new TREE %u with node `%s'\n", nsn->tree_id,
+                  nsn->id);
+      /* put all nodes with same identifier into this TREE */
+      GNUNET_CRYPTO_hash (nsn->id, strlen (nsn->id), &hc);
+      fc.id = nsn->tree_id;
+      fc.nug = nug;
+      fc.uig = uig;
+      GNUNET_CONTAINER_multihashmap_get_multiple (uig->update_map, &hc,
+                                                  &find_trees, &fc);
     }
     }
-  hk = GNUNET_RSA_decode_key (hke);
-  GNUNET_free (hke);
-  if (hk == NULL)
+    else
     {
     {
-      GNUNET_GE_LOG (c->ectx,
-                     GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
-                     _("Format of file `%s' is invalid, trying to remove.\n"),
-                     fileName);
-      UNLINK (fileName);
-      GNUNET_GE_BREAK (c->ectx, 0);
-      return GNUNET_SYSERR;
+      /* make head of TREE "id" */
+      fc.tree_array[fc.id] = nsn;
+      nsn->tree_id = fc.id;
     }
     }
-  GNUNET_RSA_get_public_key (hk, &pk);
-  GNUNET_RSA_free_key (hk);
-  GNUNET_hash (&pk, sizeof (GNUNET_RSA_PublicKey), &namespace);
-  if (NULL != c->cb)
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "TREE of node `%s' is %u\n", nsn->id,
+                fc.id);
+  }
+  for (i = 0; i < fc.tree_array_size; i++)
+  {
+    nsn = fc.tree_array[i];
+    if (NULL != nsn)
     {
     {
-      name = fileName;
-      while (NULL != strstr (name, DIR_SEPARATOR_STR))
-        name = 1 + strstr (name, DIR_SEPARATOR_STR);
-      if (GNUNET_OK == c->cb (&namespace, name, c->cls))
-        c->cnt++;
-      else
-        c->cnt = GNUNET_SYSERR;
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Root of TREE %u is node `%s'\n", i,
+                  nsn->id);
+      ip (ip_cls, nsn->id, nsn->uri, nsn->md, nsn->update);
     }
     }
-  else
-    c->cnt++;
-  return GNUNET_OK;
-}
-
-/**
- * Build a list of all available namespaces
- *
- * @param list where to store the names (is allocated, caller frees)
- * @return GNUNET_SYSERR on error, otherwise the number of pseudonyms in list
- */
-int
-GNUNET_ECRS_get_namespaces (struct GNUNET_GE_Context *ectx,
-                            struct GNUNET_GC_Configuration *cfg,
-                            GNUNET_ECRS_NamespaceInfoProcessor cb, void *cls)
-{
-  char *dirName;
-  struct lNCLS myCLS;
-
-  myCLS.cls = cls;
-  myCLS.cb = cb;
-  myCLS.cnt = 0;
-  myCLS.ectx = ectx;
-  myCLS.cfg = cfg;
-  dirName = getPseudonymFileName (ectx, cfg, NULL);
-  GNUNET_disk_directory_scan (ectx, dirName, &processFile_, &myCLS);
-  GNUNET_free (dirName);
-  return myCLS.cnt;
+  }
+  GNUNET_array_grow (fc.tree_array, fc.tree_array_size, 0);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Done processing TREEs\n");
+  free_update_information_graph (uig);
 }
 
 
 }
 
 
-
-/* end of namespace.c */
-#endif
+/* end of fs_namespace.c */