From 2ae973618f3b51fa9bbf5532eaa1352cafc24ecc Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 22 Aug 2009 17:57:31 +0000 Subject: [PATCH] stuff --- TODO | 4 +- src/fs/Makefile.am | 7 +- src/fs/fs.c | 65 ++ src/fs/fs.h | 12 + src/fs/fs_directory.c | 7 + src/fs/fs_download.c | 923 ++++++++++++++++++++++++++++ src/fs/fs_file_information.c | 269 ++++++++ src/fs/fs_namespace.c | 729 ++++++++++++++++++++++ src/fs/fs_publish.c | 459 ++++++++++++++ src/fs/fs_search.c | 572 +++++++++++++++++ src/fs/fs_test.c | 259 ++++++++ src/fs/fs_unindex.c | 394 ++++++++++++ src/fs/fs_uri.c | 133 ++-- src/fs/test_fs_collection.c | 100 +-- src/fs/test_fs_collection_data.conf | 9 + src/fs/test_fs_directory.c | 7 + src/fs/test_fs_download.c | 324 ++++++++++ src/fs/test_fs_download_recursive.c | 380 ++++++++++++ src/fs/test_fs_getopt.c | 7 + src/fs/test_fs_search_persistence.c | 213 +++++++ src/fs/test_fs_search_ranking.c | 250 ++++++++ src/fs/test_fs_start_stop.c | 89 +++ src/fs/test_fs_uri.c | 46 +- src/fs/test_namespace.c | 136 ++++ src/include/gnunet_fs_service.h | 107 ++-- 25 files changed, 5347 insertions(+), 154 deletions(-) create mode 100644 src/fs/fs.c create mode 100644 src/fs/fs_download.c create mode 100644 src/fs/fs_file_information.c create mode 100644 src/fs/fs_namespace.c create mode 100644 src/fs/fs_publish.c create mode 100644 src/fs/fs_search.c create mode 100644 src/fs/fs_test.c create mode 100644 src/fs/fs_unindex.c create mode 100644 src/fs/test_fs_collection_data.conf create mode 100644 src/fs/test_fs_download.c create mode 100644 src/fs/test_fs_download_recursive.c create mode 100644 src/fs/test_fs_search_persistence.c create mode 100644 src/fs/test_fs_search_ranking.c create mode 100644 src/fs/test_fs_start_stop.c create mode 100644 src/fs/test_namespace.c diff --git a/TODO b/TODO index ebccd0a39..8b400d5de 100644 --- a/TODO +++ b/TODO @@ -39,8 +39,8 @@ PHASE #2: (Goal: recover basic file-sharing functionality) - review FS API [Nils, Amatus, CG] - design network structs (CS) - implement FS library - + URI API - + getopt API + + URI API -- DONE (but do more testing) + + getopt API -- DONE (but do more testing) + persistence mechanism + sharing API ~ file-information diff --git a/src/fs/Makefile.am b/src/fs/Makefile.am index 99b631668..9fca261cb 100644 --- a/src/fs/Makefile.am +++ b/src/fs/Makefile.am @@ -13,9 +13,13 @@ endif lib_LTLIBRARIES = libgnunetfs.la libgnunetfs_la_SOURCES = \ + fs.c \ fs_collection.c \ fs_directory.c \ + fs_file_information.c \ fs_getopt.c \ + fs_publish.c \ + fs_namespace.c \ fs_uri.c libgnunetfs_la_LIBADD = \ @@ -31,8 +35,8 @@ libgnunetfs_la_LDFLAGS = \ # gnunet-directory # gnunet-download # gnunet-pseudonym +# gnunet-publish # gnunet-search -# gnunet-share # gnunet-unindex #gnunet_directory_SOURCES = @@ -76,4 +80,5 @@ test_fs_uri_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la EXTRA_DIST = \ + test_fs_collection_data.conf \ test_fs_uri_data.conf diff --git a/src/fs/fs.c b/src/fs/fs.c new file mode 100644 index 000000000..0522b7941 --- /dev/null +++ b/src/fs/fs.c @@ -0,0 +1,65 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009 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 fs/fs.c + * @brief main FS functions + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_fs_service.h" +#include "fs.h" + + + +/** + * Setup a connection to the file-sharing service. + * + * @param sched scheduler to use + * @param cfg configuration to use + * @param client_name unique identifier for this client + * @param upcb function to call to notify about FS actions + * @param upcb_cls closure for upcb + */ +struct GNUNET_FS_Handle * +GNUNET_FS_start (struct GNUNET_SCHEDULER_Handle *sched, + const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *client_name, + GNUNET_FS_ProgressCallback upcb, + void *upcb_cls) +{ + return NULL; +} + + +/** + * Close our connection with the file-sharing service. + * The callback given to GNUNET_FS_start will no longer be + * called after this function returns. + * + * @param h handle that was returned from GNUNET_FS_start + */ +void +GNUNET_FS_stop (struct GNUNET_FS_Handle *h) +{ +} + +/* end of fs.c */ diff --git a/src/fs/fs.h b/src/fs/fs.h index 13129be58..024e0ecc8 100644 --- a/src/fs/fs.h +++ b/src/fs/fs.h @@ -129,4 +129,16 @@ struct GNUNET_FS_Uri }; + +/** + * Information for a file or directory that is + * about to be published. + */ +struct GNUNET_FS_FileInformation +{ + + +}; + + #endif diff --git a/src/fs/fs_directory.c b/src/fs/fs_directory.c index 1ee707cd1..3eb3af50d 100644 --- a/src/fs/fs_directory.c +++ b/src/fs/fs_directory.c @@ -231,6 +231,13 @@ GNUNET_FS_directory_list_contents (size_t size, } } + +void +GNUNET_FS_directory_create () +{ +} + + #if 0 diff --git a/src/fs/fs_download.c b/src/fs/fs_download.c new file mode 100644 index 000000000..cb3b39995 --- /dev/null +++ b/src/fs/fs_download.c @@ -0,0 +1,923 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008 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/download.c + * @brief DOWNLOAD helper methods (which do the real work). + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_protocols.h" +#include "gnunet_ecrs_lib.h" +#include "gnunet_fs_lib.h" +#include "gnunet_identity_lib.h" +#include "ecrs_core.h" +#include "ecrs.h" +#include "fs.h" +#include "tree.h" + +#define DEBUG_DOWNLOAD GNUNET_NO + +/** + * Node-specific data (not shared, keep small!). 152 bytes. + * Nodes are kept in a doubly-linked list. + */ +struct Node +{ + /** + * Pointer to shared data between all nodes (request manager, + * progress data, etc.). + */ + struct GNUNET_ECRS_DownloadContext *ctx; + + /** + * Previous entry in DLL. + */ + struct Node *prev; + + /** + * Next entry in DLL. + */ + struct Node *next; + + /** + * What is the GNUNET_EC_ContentHashKey for this block? + */ + GNUNET_EC_ContentHashKey chk; + + /** + * At what offset (on the respective level!) is this + * block? + */ + unsigned long long offset; + + /** + * 0 for dblocks, >0 for iblocks. + */ + unsigned int level; + +}; + +/** + * @brief structure that keeps track of currently pending requests for + * a download + * + * Handle to the state of a request manager. Here we keep track of + * which queries went out with which priorities and which nodes in + * the merkle-tree are waiting for the replies. + */ +struct GNUNET_ECRS_DownloadContext +{ + + /** + * Total number of bytes in the file. + */ + unsigned long long total; + + /** + * Number of bytes already obtained + */ + unsigned long long completed; + + /** + * Starting-offset in file (for partial download) + */ + unsigned long long offset; + + /** + * Length of the download (starting at offset). + */ + unsigned long long length; + + /** + * Time download was started. + */ + GNUNET_CronTime startTime; + + /** + * Doubly linked list of all pending requests (head) + */ + struct Node *head; + + /** + * Doubly linked list of all pending requests (tail) + */ + struct Node *tail; + + /** + * FSLIB context for issuing requests. + */ + struct GNUNET_FS_SearchContext *sctx; + + /** + * Context for error reporting. + */ + struct GNUNET_GE_Context *ectx; + + /** + * Configuration information. + */ + struct GNUNET_GC_Configuration *cfg; + + /** + * The file handle. + */ + int handle; + + /** + * Do we exclusively own this sctx? + */ + int my_sctx; + + /** + * The base-filename + */ + char *filename; + + /** + * Main thread running the operation. + */ + struct GNUNET_ThreadHandle *main; + + /** + * Function to call when we make progress. + */ + GNUNET_ECRS_DownloadProgressCallback dpcb; + + /** + * Extra argument to dpcb. + */ + void *dpcbClosure; + + /** + * Identity of the peer having the content, or all-zeros + * if we don't know of such a peer. + */ + GNUNET_PeerIdentity target; + + /** + * Abort? Flag that can be set at any time + * to abort the RM as soon as possible. Set + * to GNUNET_YES during orderly shutdown, + * set to GNUNET_SYSERR on error. + */ + int abortFlag; + + /** + * Do we have a specific peer from which we download + * from? + */ + int have_target; + + /** + * Desired anonymity level for the download. + */ + unsigned int anonymityLevel; + + /** + * The depth of the file-tree. + */ + unsigned int treedepth; + +}; + +static int +content_receive_callback (const GNUNET_HashCode * query, + const GNUNET_DatastoreValue * reply, void *cls, + unsigned long long uid); + + +/** + * Close the files and free the associated resources. + * + * @param self reference to the download context + */ +static void +free_request_manager (struct GNUNET_ECRS_DownloadContext *rm) +{ + struct Node *pos; + + if (rm->abortFlag == GNUNET_NO) + rm->abortFlag = GNUNET_YES; + if (rm->my_sctx == GNUNET_YES) + GNUNET_FS_destroy_search_context (rm->sctx); + else + GNUNET_FS_suspend_search_context (rm->sctx); + while (rm->head != NULL) + { + pos = rm->head; + GNUNET_DLL_remove (rm->head, rm->tail, pos); + if (rm->my_sctx != GNUNET_YES) + GNUNET_FS_stop_search (rm->sctx, &content_receive_callback, pos); + GNUNET_free (pos); + } + if (rm->my_sctx != GNUNET_YES) + GNUNET_FS_resume_search_context (rm->sctx); + GNUNET_GE_ASSERT (NULL, rm->tail == NULL); + if (rm->handle >= 0) + CLOSE (rm->handle); + if (rm->main != NULL) + GNUNET_thread_release_self (rm->main); + GNUNET_free_non_null (rm->filename); + rm->sctx = NULL; + GNUNET_free (rm); +} + +/** + * Read method. + * + * @param self reference to the download context + * @param level level in the tree to read/write at + * @param pos position where to read or write + * @param buf where to read from or write to + * @param len how many bytes to read or write + * @return number of bytes read, GNUNET_SYSERR on error + */ +static int +read_from_files (struct GNUNET_ECRS_DownloadContext *self, + unsigned int level, + unsigned long long pos, void *buf, unsigned int len) +{ + if ((level > 0) || (self->handle == -1)) + return GNUNET_SYSERR; + LSEEK (self->handle, pos, SEEK_SET); + return READ (self->handle, buf, len); +} + +/** + * Write method. + * + * @param self reference to the download context + * @param level level in the tree to write to + * @param pos position where to write + * @param buf where to write to + * @param len how many bytes to write + * @return number of bytes written, GNUNET_SYSERR on error + */ +static int +write_to_files (struct GNUNET_ECRS_DownloadContext *self, + unsigned int level, + unsigned long long pos, void *buf, unsigned int len) +{ + int ret; + + if (level > 0) + return len; /* lie -- no more temps */ + if (self->handle == -1) + return len; + LSEEK (self->handle, pos, SEEK_SET); + ret = WRITE (self->handle, buf, len); + if (ret != len) + GNUNET_GE_LOG_STRERROR_FILE (self->ectx, + GNUNET_GE_ERROR | GNUNET_GE_BULK | + GNUNET_GE_USER, "write", self->filename); + return ret; +} + +/** + * Queue a request for execution. + * + * @param rm the request manager struct from createRequestManager + * @param node the node to call once a reply is received + */ +static void +add_request (struct Node *node) +{ + struct GNUNET_ECRS_DownloadContext *rm = node->ctx; + + GNUNET_DLL_insert (rm->head, rm->tail, node); + GNUNET_FS_start_search (rm->sctx, + rm->have_target == GNUNET_NO ? NULL : &rm->target, + GNUNET_ECRS_BLOCKTYPE_DATA, 1, + &node->chk.query, + rm->anonymityLevel, + &content_receive_callback, node); +} + +static void +signal_abort (struct GNUNET_ECRS_DownloadContext *rm, const char *msg) +{ + rm->abortFlag = GNUNET_SYSERR; + if ((rm->head != NULL) && (rm->dpcb != NULL)) + rm->dpcb (rm->length + 1, 0, 0, 0, msg, 0, rm->dpcbClosure); + GNUNET_thread_stop_sleep (rm->main); +} + +/** + * Dequeue a request. + * + * @param self the request manager struct from createRequestManager + * @param node the block for which the request is canceled + */ +static void +delete_node (struct Node *node) +{ + struct GNUNET_ECRS_DownloadContext *rm = node->ctx; + + GNUNET_DLL_remove (rm->head, rm->tail, node); + GNUNET_free (node); + if (rm->head == NULL) + GNUNET_thread_stop_sleep (rm->main); +} + +/** + * Compute how many bytes of data are stored in + * this node. + */ +static unsigned int +get_node_size (const struct Node *node) +{ + unsigned int i; + unsigned int ret; + unsigned long long rsize; + unsigned long long spos; + unsigned long long epos; + + GNUNET_GE_ASSERT (node->ctx->ectx, node->offset < node->ctx->total); + if (node->level == 0) + { + ret = GNUNET_ECRS_DBLOCK_SIZE; + if (node->offset + (unsigned long long) ret > node->ctx->total) + ret = (unsigned int) (node->ctx->total - node->offset); +#if DEBUG_DOWNLOAD + GNUNET_GE_LOG (node->ctx->rm->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "Node at offset %llu and level %d has size %u\n", + node->offset, node->level, ret); +#endif + return ret; + } + rsize = GNUNET_ECRS_DBLOCK_SIZE; + for (i = 0; i < node->level - 1; i++) + rsize *= GNUNET_ECRS_CHK_PER_INODE; + spos = rsize * (node->offset / sizeof (GNUNET_EC_ContentHashKey)); + epos = spos + rsize * GNUNET_ECRS_CHK_PER_INODE; + if (epos > node->ctx->total) + epos = node->ctx->total; + ret = (epos - spos) / rsize; + if (ret * rsize < epos - spos) + ret++; /* need to round up! */ +#if DEBUG_DOWNLOAD + GNUNET_GE_LOG (node->ctx->rm->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "Node at offset %llu and level %d has size %u\n", + node->offset, node->level, + ret * sizeof (GNUNET_EC_ContentHashKey)); +#endif + return ret * sizeof (GNUNET_EC_ContentHashKey); +} + +/** + * Notify client about progress. + */ +static void +notify_client_about_progress (const struct Node *node, + const char *data, unsigned int size) +{ + struct GNUNET_ECRS_DownloadContext *rm = node->ctx; + GNUNET_CronTime eta; + + if ((rm->abortFlag != GNUNET_NO) || (node->level != 0)) + return; + rm->completed += size; + eta = GNUNET_get_time (); + if (rm->completed > 0) + eta = (GNUNET_CronTime) (rm->startTime + + (((double) (eta - rm->startTime) / + (double) rm->completed)) * + (double) rm->length); + if (rm->dpcb != NULL) + rm->dpcb (rm->length, + rm->completed, eta, node->offset, data, size, rm->dpcbClosure); +} + + +/** + * DOWNLOAD children of this GNUNET_EC_IBlock. + * + * @param node the node for which the children should be downloaded + * @param data data for the node + * @param size size of data + */ +static void iblock_download_children (const struct Node *node, + const char *data, unsigned int size); + +/** + * Check if self block is already present on the drive. If the block + * is a dblock and present, the ProgressModel is notified. If the + * block is present and it is an iblock, downloading the children is + * triggered. + * + * Also checks if the block is within the range of blocks + * that we are supposed to download. If not, the method + * returns as if the block is present but does NOT signal + * progress. + * + * @param node that is checked for presence + * @return GNUNET_YES if present, GNUNET_NO if not. + */ +static int +check_node_present (const struct Node *node) +{ + int res; + int ret; + char *data; + unsigned int size; + GNUNET_HashCode hc; + + size = get_node_size (node); + /* first check if node is within range. + For now, keeping it simple, we only do + this for level-0 nodes */ + if ((node->level == 0) && + ((node->offset + size < node->ctx->offset) || + (node->offset >= node->ctx->offset + node->ctx->length))) + return GNUNET_YES; + data = GNUNET_malloc (size); + ret = GNUNET_NO; + res = read_from_files (node->ctx, node->level, node->offset, data, size); + if (res == size) + { + GNUNET_hash (data, size, &hc); + if (0 == memcmp (&hc, &node->chk.key, sizeof (GNUNET_HashCode))) + { + notify_client_about_progress (node, data, size); + if (node->level > 0) + iblock_download_children (node, data, size); + ret = GNUNET_YES; + } + } + GNUNET_free (data); + return ret; +} + +/** + * DOWNLOAD children of this GNUNET_EC_IBlock. + * + * @param node the node that should be downloaded + */ +static void +iblock_download_children (const struct Node *node, + const char *data, unsigned int size) +{ + struct GNUNET_GE_Context *ectx = node->ctx->ectx; + int i; + struct Node *child; + unsigned int childcount; + const GNUNET_EC_ContentHashKey *chks; + unsigned int levelSize; + unsigned long long baseOffset; + + GNUNET_GE_ASSERT (ectx, node->level > 0); + childcount = size / sizeof (GNUNET_EC_ContentHashKey); + if (size != childcount * sizeof (GNUNET_EC_ContentHashKey)) + { + GNUNET_GE_BREAK (ectx, 0); + return; + } + if (node->level == 1) + { + levelSize = GNUNET_ECRS_DBLOCK_SIZE; + baseOffset = + node->offset / sizeof (GNUNET_EC_ContentHashKey) * + GNUNET_ECRS_DBLOCK_SIZE; + } + else + { + levelSize = + sizeof (GNUNET_EC_ContentHashKey) * GNUNET_ECRS_CHK_PER_INODE; + baseOffset = node->offset * GNUNET_ECRS_CHK_PER_INODE; + } + chks = (const GNUNET_EC_ContentHashKey *) data; + for (i = 0; i < childcount; i++) + { + child = GNUNET_malloc (sizeof (struct Node)); + child->ctx = node->ctx; + child->chk = chks[i]; + child->offset = baseOffset + i * levelSize; + GNUNET_GE_ASSERT (ectx, child->offset < node->ctx->total); + child->level = node->level - 1; + GNUNET_GE_ASSERT (ectx, (child->level != 0) || + ((child->offset % GNUNET_ECRS_DBLOCK_SIZE) == 0)); + if (GNUNET_NO == check_node_present (child)) + add_request (child); + else + GNUNET_free (child); /* done already! */ + } +} + + +/** + * Decrypts a given data block + * + * @param data represents the data block + * @param hashcode represents the key concatenated with the initial + * value used in the alg + * @param result where to store the result (encrypted block) + * @returns GNUNET_OK on success, GNUNET_SYSERR on error + */ +static int +decrypt_content (const char *data, + unsigned int size, const GNUNET_HashCode * hashcode, + char *result) +{ + GNUNET_AES_InitializationVector iv; + GNUNET_AES_SessionKey skey; + + /* get key and init value from the GNUNET_HashCode */ + GNUNET_hash_to_AES_key (hashcode, &skey, &iv); + return GNUNET_AES_decrypt (&skey, data, size, &iv, result); +} + +/** + * We received a GNUNET_EC_ContentHashKey reply for a block. Decrypt. Note + * that the caller (fslib) has already aquired the + * RM lock (we sometimes aquire it again in callees, + * mostly because our callees could be also be theoretically + * called from elsewhere). + * + * @param cls the node for which the reply is given, freed in + * the function! + * @param query the query for which reply is the answer + * @param reply the reply + * @return GNUNET_OK if the reply was valid, GNUNET_SYSERR on error + */ +static int +content_receive_callback (const GNUNET_HashCode * query, + const GNUNET_DatastoreValue * reply, void *cls, + unsigned long long uid) +{ + struct Node *node = cls; + struct GNUNET_ECRS_DownloadContext *rm = node->ctx; + struct GNUNET_GE_Context *ectx = rm->ectx; + GNUNET_HashCode hc; + unsigned int size; + char *data; + + if (rm->abortFlag != GNUNET_NO) + return GNUNET_SYSERR; + GNUNET_GE_ASSERT (ectx, + 0 == memcmp (query, &node->chk.query, + sizeof (GNUNET_HashCode))); + size = ntohl (reply->size) - sizeof (GNUNET_DatastoreValue); + if ((size <= sizeof (GNUNET_EC_DBlock)) || + (size - sizeof (GNUNET_EC_DBlock) != get_node_size (node))) + { + GNUNET_GE_BREAK (ectx, 0); + return GNUNET_SYSERR; /* invalid size! */ + } + size -= sizeof (GNUNET_EC_DBlock); + data = GNUNET_malloc (size); + if (GNUNET_SYSERR == + decrypt_content ((const char *) + &((const GNUNET_EC_DBlock *) &reply[1])[1], size, + &node->chk.key, data)) + GNUNET_GE_ASSERT (ectx, 0); + GNUNET_hash (data, size, &hc); + if (0 != memcmp (&hc, &node->chk.key, sizeof (GNUNET_HashCode))) + { + GNUNET_free (data); + GNUNET_GE_BREAK (ectx, 0); + signal_abort (rm, + _("Decrypted content does not match key. " + "This is either a bug or a maliciously inserted " + "file. Download aborted.\n")); + return GNUNET_SYSERR; + } + if (size != write_to_files (rm, node->level, node->offset, data, size)) + { + GNUNET_GE_LOG_STRERROR (ectx, + GNUNET_GE_ERROR | GNUNET_GE_ADMIN | + GNUNET_GE_USER | GNUNET_GE_BULK, "WRITE"); + signal_abort (rm, _("IO error.")); + return GNUNET_SYSERR; + } + notify_client_about_progress (node, data, size); + if (node->level > 0) + iblock_download_children (node, data, size); + GNUNET_free (data); + /* request satisfied, stop requesting! */ + delete_node (node); + return GNUNET_OK; +} + + +/** + * Helper function to sanitize filename + * and create necessary directories. + */ +static char * +get_real_download_filename (struct GNUNET_GE_Context *ectx, + const char *filename) +{ + struct stat buf; + char *realFN; + char *path; + char *pos; + + if ((filename[strlen (filename) - 1] == '/') || + (filename[strlen (filename) - 1] == '\\')) + { + realFN = + GNUNET_malloc (strlen (filename) + strlen (GNUNET_DIRECTORY_EXT)); + strcpy (realFN, filename); + realFN[strlen (filename) - 1] = '\0'; + strcat (realFN, GNUNET_DIRECTORY_EXT); + } + else + { + realFN = GNUNET_strdup (filename); + } + path = GNUNET_malloc (strlen (realFN) * strlen (GNUNET_DIRECTORY_EXT) + 1); + strcpy (path, realFN); + pos = path; + while (*pos != '\0') + { + if (*pos == DIR_SEPARATOR) + { + *pos = '\0'; + if ((0 == STAT (path, &buf)) && (!S_ISDIR (buf.st_mode))) + { + *pos = DIR_SEPARATOR; + memmove (pos + strlen (GNUNET_DIRECTORY_EXT), + pos, strlen (pos)); + memcpy (pos, + GNUNET_DIRECTORY_EXT, strlen (GNUNET_DIRECTORY_EXT)); + pos += strlen (GNUNET_DIRECTORY_EXT); + } + else + { + *pos = DIR_SEPARATOR; + } + } + pos++; + } + GNUNET_free (realFN); + return path; +} + +/* ***************** main method **************** */ + + +/** + * Download parts of a file. Note that this will store + * the blocks at the respective offset in the given file. + * Also, the download is still using the blocking of the + * underlying ECRS encoding. As a result, the download + * may *write* outside of the given boundaries (if offset + * and length do not match the 32k ECRS block boundaries). + *

+ * + * This function should be used to focus a download towards a + * particular portion of the file (optimization), not to strictly + * limit the download to exactly those bytes. + * + * @param uri the URI of the file (determines what to download) + * @param filename where to store the file + * @param no_temporaries set to GNUNET_YES to disallow generation of temporary files + * @param start starting offset + * @param length length of the download (starting at offset) + */ +struct GNUNET_ECRS_DownloadContext * +GNUNET_ECRS_file_download_partial_start (struct GNUNET_GE_Context *ectx, + struct GNUNET_GC_Configuration *cfg, + struct GNUNET_FS_SearchContext *sc, + const struct GNUNET_ECRS_URI *uri, + const char *filename, + unsigned long long offset, + unsigned long long length, + unsigned int anonymityLevel, + int no_temporaries, + GNUNET_ECRS_DownloadProgressCallback + dpcb, void *dpcbClosure) +{ + struct GNUNET_ECRS_DownloadContext *rm; + struct stat buf; + struct Node *top; + int ret; + + if ((!GNUNET_ECRS_uri_test_chk (uri)) && (!GNUNET_ECRS_uri_test_loc (uri))) + { + GNUNET_GE_BREAK (ectx, 0); + return NULL; + } + rm = GNUNET_malloc (sizeof (struct GNUNET_ECRS_DownloadContext)); + memset (rm, 0, sizeof (struct GNUNET_ECRS_DownloadContext)); + if (sc == NULL) + { + rm->sctx = GNUNET_FS_create_search_context (ectx, cfg); + if (rm->sctx == NULL) + { + GNUNET_free (rm); + return NULL; + } + rm->my_sctx = GNUNET_YES; + } + else + { + rm->sctx = sc; + rm->my_sctx = GNUNET_NO; + } + rm->ectx = ectx; + rm->cfg = cfg; + rm->startTime = GNUNET_get_time (); + rm->anonymityLevel = anonymityLevel; + rm->offset = offset; + rm->length = length; + rm->dpcb = dpcb; + rm->dpcbClosure = dpcbClosure; + rm->main = GNUNET_thread_get_self (); + rm->total = GNUNET_ntohll (uri->data.fi.file_length); + rm->filename = + filename != NULL ? get_real_download_filename (ectx, filename) : NULL; + + if ((rm->filename != NULL) && + (GNUNET_SYSERR == + GNUNET_disk_directory_create_for_file (ectx, rm->filename))) + { + free_request_manager (rm); + return NULL; + } + if (0 == rm->total) + { + if (rm->filename != NULL) + { + ret = GNUNET_disk_file_open (ectx, + rm->filename, + O_CREAT | O_WRONLY | O_TRUNC, + S_IRUSR | S_IWUSR); + if (ret == -1) + { + free_request_manager (rm); + return NULL; + } + CLOSE (ret); + } + dpcb (0, 0, rm->startTime, 0, NULL, 0, dpcbClosure); + free_request_manager (rm); + return NULL; + } + rm->treedepth = GNUNET_ECRS_compute_depth (rm->total); + if ((NULL != rm->filename) && + ((0 == STAT (rm->filename, &buf)) + && ((size_t) buf.st_size > rm->total))) + { + /* if exists and oversized, truncate */ + if (truncate (rm->filename, rm->total) != 0) + { + GNUNET_GE_LOG_STRERROR_FILE (ectx, + GNUNET_GE_ERROR | GNUNET_GE_ADMIN | + GNUNET_GE_BULK, "truncate", + rm->filename); + free_request_manager (rm); + return NULL; + } + } + if (rm->filename != NULL) + { + rm->handle = GNUNET_disk_file_open (ectx, + rm->filename, + O_CREAT | O_RDWR, + S_IRUSR | S_IWUSR); + if (rm->handle < 0) + { + free_request_manager (rm); + return NULL; + } + } + else + rm->handle = -1; + if (GNUNET_ECRS_uri_test_loc (uri)) + { + GNUNET_hash (&uri->data.loc.peer, sizeof (GNUNET_RSA_PublicKey), + &rm->target.hashPubKey); + rm->have_target = GNUNET_YES; + } + top = GNUNET_malloc (sizeof (struct Node)); + memset (top, 0, sizeof (struct Node)); + top->ctx = rm; + top->chk = uri->data.fi.chk; + top->offset = 0; + top->level = rm->treedepth; + if (GNUNET_NO == check_node_present (top)) + add_request (top); + else + GNUNET_free (top); + return rm; +} + +int +GNUNET_ECRS_file_download_partial_stop (struct GNUNET_ECRS_DownloadContext + *rm) +{ + int ret; + + ret = rm->abortFlag; + free_request_manager (rm); + if (ret == GNUNET_NO) + ret = GNUNET_OK; /* normal termination */ + return ret; +} + +/** + * Download parts of a file. Note that this will store + * the blocks at the respective offset in the given file. + * Also, the download is still using the blocking of the + * underlying ECRS encoding. As a result, the download + * may *write* outside of the given boundaries (if offset + * and length do not match the 32k ECRS block boundaries). + *

+ * + * This function should be used to focus a download towards a + * particular portion of the file (optimization), not to strictly + * limit the download to exactly those bytes. + * + * @param uri the URI of the file (determines what to download) + * @param filename where to store the file + * @param no_temporaries set to GNUNET_YES to disallow generation of temporary files + * @param start starting offset + * @param length length of the download (starting at offset) + */ +int +GNUNET_ECRS_file_download_partial (struct GNUNET_GE_Context *ectx, + struct GNUNET_GC_Configuration *cfg, + const struct GNUNET_ECRS_URI *uri, + const char *filename, + unsigned long long offset, + unsigned long long length, + unsigned int anonymityLevel, + int no_temporaries, + GNUNET_ECRS_DownloadProgressCallback dpcb, + void *dpcbClosure, + GNUNET_ECRS_TestTerminate tt, + void *ttClosure) +{ + struct GNUNET_ECRS_DownloadContext *rm; + int ret; + + if (length == 0) + return GNUNET_OK; + rm = GNUNET_ECRS_file_download_partial_start (ectx, + cfg, + NULL, + uri, + filename, + offset, + length, + anonymityLevel, + no_temporaries, + dpcb, dpcbClosure); + if (rm == NULL) + return GNUNET_SYSERR; + while ((GNUNET_OK == tt (ttClosure)) && + (GNUNET_YES != GNUNET_shutdown_test ()) && + (rm->abortFlag == GNUNET_NO) && (rm->head != NULL)) + GNUNET_thread_sleep (5 * GNUNET_CRON_SECONDS); + ret = GNUNET_ECRS_file_download_partial_stop (rm); + return ret; +} + +/** + * Download a file (simplified API). + * + * @param uri the URI of the file (determines what to download) + * @param filename where to store the file + */ +int +GNUNET_ECRS_file_download (struct GNUNET_GE_Context *ectx, + struct GNUNET_GC_Configuration *cfg, + const struct GNUNET_ECRS_URI *uri, + const char *filename, + unsigned int anonymityLevel, + GNUNET_ECRS_DownloadProgressCallback dpcb, + void *dpcbClosure, GNUNET_ECRS_TestTerminate tt, + void *ttClosure) +{ + return GNUNET_ECRS_file_download_partial (ectx, + cfg, + uri, + filename, + 0, + GNUNET_ECRS_uri_get_file_size + (uri), anonymityLevel, GNUNET_NO, + dpcb, dpcbClosure, tt, ttClosure); +} + +/* end of download.c */ diff --git a/src/fs/fs_file_information.c b/src/fs/fs_file_information.c new file mode 100644 index 000000000..d565f4397 --- /dev/null +++ b/src/fs/fs_file_information.c @@ -0,0 +1,269 @@ +/* + This file is part of GNUnet. + (C) 2009 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 fs/fs_file_information.c + * @brief Manage information for publishing directory hierarchies + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_fs_service.h" +#include "fs.h" + + +/** + * Create an entry for a file in a publish-structure. + * + * @param filename name of the file or directory to publish + * @param meta metadata for the file + * @param do_index GNUNET_YES for index, GNUNET_NO for insertion, + * GNUNET_SYSERR for simulation + * @param anonymity what is the desired anonymity level for sharing? + * @param priority what is the priority for OUR node to + * keep this file available? Use 0 for maximum anonymity and + * minimum reliability... + * @param expirationTime when should this content expire? + * @return publish structure entry for the file + */ +struct GNUNET_FS_FileInformation * +GNUNET_FS_file_information_create_from_file (void *client_info, + const char *filename, + const struct GNUNET_CONTAINER_MetaData *meta, + int do_index, + unsigned int anonymity, + unsigned int priority, + struct GNUNET_TIME_Absolute expirationTime) +{ + return NULL; +} + +/** + * Create an entry for a file in a publish-structure. + * + * @param length length of the file + * @param data data for the file (should not be used afterwards by + * the caller; caller will "free") + * @param meta metadata for the file + * @param do_index GNUNET_YES for index, GNUNET_NO for insertion, + * GNUNET_SYSERR for simulation + * @param anonymity what is the desired anonymity level for sharing? + * @param priority what is the priority for OUR node to + * keep this file available? Use 0 for maximum anonymity and + * minimum reliability... + * @param expirationTime when should this content expire? + * @return publish structure entry for the file + */ +struct GNUNET_FS_FileInformation * +GNUNET_FS_file_information_create_from_data (void *client_info, + uint64_t length, + void *data, + const struct GNUNET_CONTAINER_MetaData *meta, + int do_index, + unsigned int anonymity, + unsigned int priority, + struct GNUNET_TIME_Absolute expirationTime) +{ + return NULL; +} + + +/** + * Create an entry for a file in a publish-structure. + * + * @param length length of the file + * @param reader function that can be used to obtain the data for the file + * @param reader_cls closure for "reader" + * @param keywords under which keywords should this file be available + * directly; can be NULL + * @param meta metadata for the file + * @param do_index GNUNET_YES for index, GNUNET_NO for insertion, + * GNUNET_SYSERR for simulation + * @param anonymity what is the desired anonymity level for sharing? + * @param priority what is the priority for OUR node to + * keep this file available? Use 0 for maximum anonymity and + * minimum reliability... + * @param expirationTime when should this content expire? + * @return publish structure entry for the file + */ +struct GNUNET_FS_FileInformation * +GNUNET_FS_file_information_create_from_reader (void *client_info, + uint64_t length, + GNUNET_FS_DataReader reader, + void *reader_cls, + const struct GNUNET_FS_Uri *keywords, + const struct GNUNET_CONTAINER_MetaData *meta, + int do_index, + unsigned int anonymity, + unsigned int priority, + struct GNUNET_TIME_Absolute expirationTime) +{ + return NULL; +} + + + +/** + * Simple, useful default implementation of a directory scanner + * (GNUNET_FS_DirectoryScanner). This implementation expects to get a + * UNIX filename, will publish all files in the directory except hidden + * files (those starting with a "."). Metadata will be extracted + * using GNU libextractor; the specific list of plugins should be + * specified in "cls", passing NULL will disable (!) metadata + * extraction. Keywords will be derived from the metadata and be + * subject to default canonicalization. This is strictly a + * convenience function. + * + * @param cls must be of type "struct EXTRACTOR_Extractor*" + * @param dirname name of the directory to scan + * @param proc function called on each entry + * @param proc_cls closure for proc + * @param emsg where to store an error message (on errors) + * @return GNUNET_OK on success + */ +int +GNUNET_FS_directory_scanner_default (void *cls, + const char *dirname, + GNUNET_FS_FileProcessor proc, + void *proc_cls) +{ + return GNUNET_SYSERR; +} + + +/** + * Create a publish-structure from an existing file hierarchy, inferring + * and organizing keywords and metadata as much as possible. This + * function primarily performs the recursive build and re-organizes + * keywords and metadata; for automatically getting metadata + * extraction, scanning of directories and creation of the respective + * GNUNET_FS_FileInformation entries the default scanner should be + * passed (GNUNET_FS_directory_scanner_default). This is strictly a + * convenience function. + * + * @param filename name of the top-level file or directory + * @param scanner function used to get a list of files in a directory + * @param scanner_cls closure for scanner + * @param anonymity what is the desired anonymity level for sharing? + * @param priority what is the priority for OUR node to + * keep this file available? Use 0 for maximum anonymity and + * minimum reliability... + * @param expirationTime when should this content expire? + * @return publish structure entry for the directory, NULL on error + */ +struct GNUNET_FS_FileInformation * +GNUNET_FS_file_information_create_from_directory (void *client_info, + GNUNET_FS_DirectoryScanner scanner, + void *scanner_cls, + unsigned int anonymity, + unsigned int priority, + struct GNUNET_TIME_Absolute expirationTime) +{ + return NULL; +} + + +/** + * Create an entry for an empty directory in a publish-structure. + * This function should be used by applications for which the + * use of "GNUNET_FS_file_information_create_from_directory" + * is not appropriate. + * + * @param meta metadata for the directory + * @param keywords under which keywords should this directory be available + * directly; can be NULL + * @param anonymity what is the desired anonymity level for sharing? + * @param priority what is the priority for OUR node to + * keep this file available? Use 0 for maximum anonymity and + * minimum reliability... + * @param expirationTime when should this content expire? + * @return publish structure entry for the directory , NULL on error + */ +struct GNUNET_FS_FileInformation * +GNUNET_FS_file_information_create_empty_directory (void *client_info, + const struct GNUNET_CONTAINER_MetaData *meta, + const struct GNUNET_FS_Uri *keywords, + unsigned int anonymity, + unsigned int priority, + struct GNUNET_TIME_Absolute expirationTime) +{ + return NULL; +} + + +/** + * Add an entry to a directory in a publish-structure. Clients + * should never modify publish structures that were passed to + * "GNUNET_FS_publish_start" already. + * + * @param dir the directory + * @param end the entry to add; the entry must not have been + * added to any other directory at this point and + * must not include "dir" in its structure + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int +GNUNET_FS_file_information_add (struct GNUNET_FS_FileInformation *dir, + struct GNUNET_FS_FileInformation *end) +{ + return GNUNET_SYSERR; +} + + +/** + * Inspect a file or directory in a publish-structure. Clients + * should never modify publish structures that were passed to + * "GNUNET_FS_publish_start" already. When called on a directory, + * this function will FIRST call "proc" with information about + * the directory itself and then for each of the files in the + * directory (but not for files in subdirectories). When called + * on a file, "proc" will be called exactly once (with information + * about the specific file). + * + * @param dir the directory + * @param proc function to call on each entry + * @param proc_cls closure for proc + */ +void +GNUNET_FS_file_information_inspect (struct GNUNET_FS_FileInformation *dir, + GNUNET_FS_FileInformationProcessor proc, + void *proc_cls) +{ +} + + +/** + * Destroy publish-structure. Clients should never destroy publish + * structures that were passed to "GNUNET_FS_publish_start" already. + * + * @param fi structure to destroy + * @param cleaner function to call on each entry in the structure + * (useful to clean up client_info); can be NULL; return + * values are ignored + * @param cleaner_cls closure for cleaner + */ +void +GNUNET_FS_file_information_destroy (struct GNUNET_FS_FileInformation *fi, + GNUNET_FS_FileInformationProcessor cleaner, + void *cleaner_cls) +{ +} + + +/* end of fs_file_information.c */ diff --git a/src/fs/fs_namespace.c b/src/fs/fs_namespace.c new file mode 100644 index 000000000..fb45d99e9 --- /dev/null +++ b/src/fs/fs_namespace.c @@ -0,0 +1,729 @@ +/* + This file is part of GNUnet + (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 fs/fs_namespace.c + * @brief create and destroy namespaces + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_fs_service.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 + */ +struct GNUNET_FS_Uri * +GNUNET_FS_namespace_advertise (struct GNUNET_FS_Handle *h, + struct GNUNET_FS_Namespace *namespace, + const struct GNUNET_MetaData *meta, + unsigned int anonymity, + unsigned int priority, + struct GNUNET_TIME_Absolute expiration, + const struct GNUNET_FS_Uri *advertisementURI, + const char *rootEntry) +{ + return NULL; +} + + +/** + * 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 + */ +struct GNUNET_FS_Namespace * +GNUNET_FS_namespace_create (struct GNUNET_FS_Handle *h, + const char *name) +{ + return NULL; +} + + +/** + * 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 GNUNET_OK on success, GNUNET_SYSERR on error + */ +int +GNUNET_FS_namespace_delete (struct GNUNET_FS_Namespace *namespace, + int freeze) +{ + return GNUNET_SYSERR; +} + + +/** + * Build a list of all available local (!) namespaces The returned + * names are only the nicknames since we only iterate over the local + * namespaces. + * + * @param h handle to the file sharing subsystem + * @param cb function to call on each known namespace + * @param cb_cls closure for cb + */ +void +GNUNET_FS_namespace_list (struct GNUNET_FS_Handle *h, + GNUNET_FS_NamespaceInfoProcessor cb, + void *cb_cls) +{ +} + +/* 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 + */ + +#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) +{ + 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) + { + GNUNET_hash_to_enc (pid, &enc); + strcat (fileName, (char *) &enc); + } + return fileName; +} + + +/** + * Check if the given namespace exists (locally). + * + * @return GNUNET_OK if the namespace exists, GNUNET_SYSERR if not + */ +int +GNUNET_ECRS_namespace_test_exists (struct GNUNET_GE_Context *ectx, + struct GNUNET_GC_Configuration *cfg, + const GNUNET_HashCode * pid) +{ + char *fileName; + int ret; + + fileName = getPseudonymFileName (ectx, cfg, pid); + ret = GNUNET_disk_file_test (ectx, fileName); + GNUNET_free (fileName); + return ret; +} + +/** + * Delete a local namespace. + * + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int +GNUNET_ECRS_namespace_delete (struct GNUNET_GE_Context *ectx, + struct GNUNET_GC_Configuration *cfg, + const GNUNET_HashCode * pid) +{ + 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; +} + +/** + * Write the private key of the namespace to a file. + */ +static int +write_namespace_key (struct GNUNET_GC_Configuration *cfg, + const struct GNUNET_RSA_PrivateKey *key) +{ + 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; +} + +/** + * 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 + * + * @return URI on success, NULL on error + */ +struct GNUNET_ECRS_URI * +GNUNET_ECRS_namespace_create (struct GNUNET_GE_Context *ectx, + struct GNUNET_GC_Configuration *cfg, + const struct GNUNET_MetaData *meta, + unsigned int anonymityLevel, + unsigned int priority, + GNUNET_CronTime expiration, + const struct GNUNET_ECRS_URI *advertisementURI, + const char *rootEntry) +{ + 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))) + { + GNUNET_GE_BREAK (ectx, 0); + 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; +} + +static struct GNUNET_RSA_PrivateKey * +read_namespace_key (struct GNUNET_GC_Configuration *cfg, + const GNUNET_HashCode * pid) +{ + 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; +} + + +/** + * 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 + */ +struct GNUNET_ECRS_URI * +GNUNET_ECRS_namespace_add_content (struct GNUNET_GE_Context *ectx, + struct GNUNET_GC_Configuration *cfg, + const GNUNET_HashCode * pid, + unsigned int anonymityLevel, + unsigned int priority, + GNUNET_CronTime expiration, + const char *thisId, + const char *nextId, + const struct GNUNET_ECRS_URI *dstU, + const struct GNUNET_MetaData *md) +{ + 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; + + /* 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; +} + +struct lNCLS +{ + struct GNUNET_GE_Context *ectx; + struct GNUNET_GC_Configuration *cfg; + GNUNET_ECRS_NamespaceInfoProcessor cb; + void *cls; + int cnt; +}; + +static int +processFile_ (void *cls, const char *fileName) +{ + 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; + + if (GNUNET_OK != + GNUNET_disk_file_size (c->ectx, fileName, &len, GNUNET_YES)) + return GNUNET_OK; + if (len < 2) + { + 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; + } + 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_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; + } + hk = GNUNET_RSA_decode_key (hke); + GNUNET_free (hke); + if (hk == NULL) + { + 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; + } + GNUNET_RSA_get_public_key (hk, &pk); + GNUNET_RSA_free_key (hk); + GNUNET_hash (&pk, sizeof (GNUNET_RSA_PublicKey), &namespace); + if (NULL != c->cb) + { + 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; + } + 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; +} + + + +/* end of namespace.c */ +#endif diff --git a/src/fs/fs_publish.c b/src/fs/fs_publish.c new file mode 100644 index 000000000..ac71c8f21 --- /dev/null +++ b/src/fs/fs_publish.c @@ -0,0 +1,459 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009 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 fs/fs_publish.c + * @brief publish a file or directory in GNUnet + * @see http://gnunet.org/encoding.php3 + * @author Krista Bennett + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_fs_service.h" +#include "fs.h" + +#define DEBUG_PUBLISH GNUNET_YES + + + +#if 0 + +/** + * Append the given key and query to the iblock[level]. If + * iblock[level] is already full, compute its chk and push it to + * level+1 and clear the level. iblocks is guaranteed to be big + * enough. + */ +static int +pushBlock (struct GNUNET_ClientServerConnection *sock, + const GNUNET_EC_ContentHashKey * chk, + unsigned int level, + GNUNET_DatastoreValue ** iblocks, + unsigned int prio, GNUNET_CronTime expirationTime) +{ + unsigned int size; + unsigned int present; + GNUNET_DatastoreValue *value; + GNUNET_EC_DBlock *db; + GNUNET_EC_ContentHashKey ichk; + + size = ntohl (iblocks[level]->size); + GNUNET_GE_ASSERT (NULL, size > sizeof (GNUNET_DatastoreValue)); + size -= sizeof (GNUNET_DatastoreValue); + GNUNET_GE_ASSERT (NULL, + size - sizeof (GNUNET_EC_DBlock) <= + GNUNET_ECRS_IBLOCK_SIZE); + present = + (size - sizeof (GNUNET_EC_DBlock)) / sizeof (GNUNET_EC_ContentHashKey); + db = (GNUNET_EC_DBlock *) & iblocks[level][1]; + if (present == GNUNET_ECRS_CHK_PER_INODE) + { + GNUNET_EC_file_block_get_key (db, size, &ichk.key); + GNUNET_EC_file_block_get_query (db, size, &ichk.query); + if (GNUNET_OK != pushBlock (sock, + &ichk, level + 1, iblocks, prio, + expirationTime)) + return GNUNET_SYSERR; + GNUNET_EC_file_block_encode (db, size, &ichk.query, &value); + if (value == NULL) + { + GNUNET_GE_BREAK (NULL, 0); + return GNUNET_SYSERR; + } + value->priority = htonl (prio); + value->expiration_time = GNUNET_htonll (expirationTime); + if (GNUNET_OK != GNUNET_FS_insert (sock, value)) + { + GNUNET_free (value); + return GNUNET_SYSERR; + } + GNUNET_free (value); + size = sizeof (GNUNET_EC_DBlock); /* type */ + } + /* append GNUNET_EC_ContentHashKey */ + memcpy (&((char *) db)[size], chk, sizeof (GNUNET_EC_ContentHashKey)); + size += sizeof (GNUNET_EC_ContentHashKey) + sizeof (GNUNET_DatastoreValue); + GNUNET_GE_ASSERT (NULL, size < GNUNET_MAX_BUFFER_SIZE); + iblocks[level]->size = htonl (size); + + return GNUNET_OK; +} + +/** + * Index or insert a file. + * + * @param priority what is the priority for OUR node to + * keep this file available? Use 0 for maximum anonymity and + * minimum reliability... + * @param doIndex GNUNET_YES for index, GNUNET_NO for insertion, + * GNUNET_SYSERR for simulation + * @param uri set to the URI of the uploaded file + * @return GNUNET_SYSERR if the upload failed (i.e. not enough space + * or gnunetd not running) + */ +int +GNUNET_ECRS_file_upload (struct GNUNET_GE_Context *ectx, + struct GNUNET_GC_Configuration *cfg, + const char *filename, + int doIndex, + unsigned int anonymityLevel, + unsigned int priority, + GNUNET_CronTime expirationTime, + GNUNET_ECRS_UploadProgressCallback upcb, + void *upcbClosure, + GNUNET_ECRS_TestTerminate tt, + void *ttClosure, struct GNUNET_ECRS_URI **uri) +{ + unsigned long long filesize; + unsigned long long pos; + unsigned int treedepth; + int fd; + int i; + int ret; + unsigned int size; + GNUNET_DatastoreValue **iblocks; + GNUNET_DatastoreValue *dblock; + GNUNET_EC_DBlock *db; + GNUNET_DatastoreValue *value; + struct GNUNET_ClientServerConnection *sock; + GNUNET_HashCode fileId; + GNUNET_EC_ContentHashKey mchk; + GNUNET_CronTime eta; + GNUNET_CronTime start; + GNUNET_CronTime now; + GNUNET_EC_FileIdentifier fid; +#if DEBUG_UPLOAD + GNUNET_EncName enc; +#endif + + GNUNET_GE_ASSERT (ectx, cfg != NULL); + start = GNUNET_get_time (); + memset (&mchk, 0, sizeof (GNUNET_EC_ContentHashKey)); + if (GNUNET_YES != GNUNET_disk_file_test (ectx, filename)) + { + GNUNET_GE_LOG (ectx, + GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER, + _("`%s' is not a file.\n"), filename); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_disk_file_size (ectx, filename, &filesize, GNUNET_YES)) + { + GNUNET_GE_LOG (ectx, + GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER, + _("Cannot get size of file `%s'"), filename); + + return GNUNET_SYSERR; + } + sock = GNUNET_client_connection_create (ectx, cfg); + if (sock == NULL) + { + GNUNET_GE_LOG (ectx, + GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER, + _("Failed to connect to gnunetd.")); + return GNUNET_SYSERR; + } + eta = 0; + if (upcb != NULL) + upcb (filesize, 0, eta, upcbClosure); + if (doIndex == GNUNET_YES) + { + if (GNUNET_SYSERR == GNUNET_hash_file (ectx, filename, &fileId)) + { + GNUNET_GE_LOG (ectx, + GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER, + _("Cannot hash `%s'.\n"), filename); + + GNUNET_client_connection_destroy (sock); + return GNUNET_SYSERR; + } + if (GNUNET_YES == GNUNET_FS_test_indexed (sock, &fileId)) + { + /* file already indexed; simulate only to get the URI! */ + doIndex = GNUNET_SYSERR; + } + } + if (doIndex == GNUNET_YES) + { + now = GNUNET_get_time (); + eta = now + 2 * (now - start); + /* very rough estimate: GNUNET_hash reads once through the file, + we'll do that once more and write it. But of course + the second read may be cached, and we have the encryption, + so a factor of two is really, really just a rough estimate */ + start = now; + /* reset the counter since the formula later does not + take the time for GNUNET_hash_file into account */ + + switch (GNUNET_FS_prepare_to_index (sock, &fileId, filename)) + { + case GNUNET_SYSERR: + GNUNET_GE_LOG (ectx, + GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER, + _("Initialization for indexing file `%s' failed.\n"), + filename); + GNUNET_client_connection_destroy (sock); + return GNUNET_SYSERR; + case GNUNET_NO: + GNUNET_GE_LOG (ectx, + GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER, + _ + ("Indexing file `%s' failed. Suggestion: try to insert the file.\n"), + filename); + GNUNET_client_connection_destroy (sock); + return GNUNET_SYSERR; + default: + break; + } + } + treedepth = GNUNET_ECRS_compute_depth (filesize); + fd = GNUNET_disk_file_open (ectx, filename, O_RDONLY | O_LARGEFILE); + if (fd == -1) + { + GNUNET_GE_LOG (ectx, + GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER, + _("Cannot open file `%s': `%s'"), filename, + STRERROR (errno)); + + GNUNET_client_connection_destroy (sock); + return GNUNET_SYSERR; + } + + dblock = + GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + GNUNET_ECRS_DBLOCK_SIZE + + sizeof (GNUNET_EC_DBlock)); + dblock->size = + htonl (sizeof (GNUNET_DatastoreValue) + GNUNET_ECRS_DBLOCK_SIZE + + sizeof (GNUNET_EC_DBlock)); + dblock->anonymity_level = htonl (anonymityLevel); + dblock->priority = htonl (priority); + dblock->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA); + dblock->expiration_time = GNUNET_htonll (expirationTime); + db = (GNUNET_EC_DBlock *) & dblock[1]; + db->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA); + iblocks = + GNUNET_malloc (sizeof (GNUNET_DatastoreValue *) * (treedepth + 1)); + for (i = 0; i <= treedepth; i++) + { + iblocks[i] = + GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + + GNUNET_ECRS_IBLOCK_SIZE + sizeof (GNUNET_EC_DBlock)); + iblocks[i]->size = + htonl (sizeof (GNUNET_DatastoreValue) + sizeof (GNUNET_EC_DBlock)); + iblocks[i]->anonymity_level = htonl (anonymityLevel); + iblocks[i]->priority = htonl (priority); + iblocks[i]->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA); + iblocks[i]->expiration_time = GNUNET_htonll (expirationTime); + ((GNUNET_EC_DBlock *) & iblocks[i][1])->type = + htonl (GNUNET_ECRS_BLOCKTYPE_DATA); + } + + pos = 0; + while (pos < filesize) + { + if (upcb != NULL) + upcb (filesize, pos, eta, upcbClosure); + if (tt != NULL) + if (GNUNET_OK != tt (ttClosure)) + goto FAILURE; + size = GNUNET_ECRS_DBLOCK_SIZE; + if (size > filesize - pos) + { + size = filesize - pos; + memset (&db[1], 0, GNUNET_ECRS_DBLOCK_SIZE); + } + GNUNET_GE_ASSERT (ectx, + sizeof (GNUNET_DatastoreValue) + size + + sizeof (GNUNET_EC_DBlock) < GNUNET_MAX_BUFFER_SIZE); + dblock->size = + htonl (sizeof (GNUNET_DatastoreValue) + size + + sizeof (GNUNET_EC_DBlock)); + if (size != READ (fd, &db[1], size)) + { + GNUNET_GE_LOG_STRERROR_FILE (ectx, + GNUNET_GE_ERROR | GNUNET_GE_BULK | + GNUNET_GE_ADMIN | GNUNET_GE_USER, + "READ", filename); + goto FAILURE; + } + if (tt != NULL) + if (GNUNET_OK != tt (ttClosure)) + goto FAILURE; + GNUNET_EC_file_block_get_key (db, size + sizeof (GNUNET_EC_DBlock), + &mchk.key); + GNUNET_EC_file_block_get_query (db, size + sizeof (GNUNET_EC_DBlock), + &mchk.query); +#if DEBUG_UPLOAD + GNUNET_hash_to_enc (&mchk.query, &enc); + fprintf (stderr, + "Query for current block of size %u is `%s'\n", size, + (const char *) &enc); +#endif + if (doIndex == GNUNET_YES) + { + if (GNUNET_SYSERR == GNUNET_FS_index (sock, &fileId, dblock, pos)) + { + GNUNET_GE_LOG (ectx, + GNUNET_GE_ERROR | GNUNET_GE_BULK | + GNUNET_GE_USER, + _ + ("Indexing data of file `%s' failed at position %llu.\n"), + filename, pos); + goto FAILURE; + } + } + else + { + value = NULL; + if (GNUNET_OK != + GNUNET_EC_file_block_encode (db, + size + sizeof (GNUNET_EC_DBlock), + &mchk.query, &value)) + { + GNUNET_GE_BREAK (ectx, 0); + goto FAILURE; + } + GNUNET_GE_ASSERT (ectx, value != NULL); + *value = *dblock; /* copy options! */ + if ((doIndex == GNUNET_NO) && + (GNUNET_OK != (ret = GNUNET_FS_insert (sock, value)))) + { + GNUNET_GE_BREAK (ectx, ret == GNUNET_NO); + GNUNET_free (value); + goto FAILURE; + } + GNUNET_free (value); + } + pos += size; + now = GNUNET_get_time (); + if (pos > 0) + { + eta = (GNUNET_CronTime) (start + + (((double) (now - start) / (double) pos)) + * (double) filesize); + } + if (GNUNET_OK != pushBlock (sock, &mchk, 0, /* dblocks are on level 0 */ + iblocks, priority, expirationTime)) + goto FAILURE; + } + if (tt != NULL) + if (GNUNET_OK != tt (ttClosure)) + goto FAILURE; +#if DEBUG_UPLOAD + GNUNET_GE_LOG (ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "Tree depth is %u, walking up tree.\n", treedepth); +#endif + for (i = 0; i < treedepth; i++) + { + size = ntohl (iblocks[i]->size) - sizeof (GNUNET_DatastoreValue); + GNUNET_GE_ASSERT (ectx, size < GNUNET_MAX_BUFFER_SIZE); + if (size == sizeof (GNUNET_EC_DBlock)) + { +#if DEBUG_UPLOAD + GNUNET_GE_LOG (ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "Level %u is empty\n", i); +#endif + continue; + } + db = (GNUNET_EC_DBlock *) & iblocks[i][1]; + GNUNET_EC_file_block_get_key (db, size, &mchk.key); +#if DEBUG_UPLOAD + GNUNET_GE_LOG (ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "Computing query for %u bytes content.\n", size); +#endif + GNUNET_EC_file_block_get_query (db, size, &mchk.query); +#if DEBUG_UPLOAD + IF_GELOG (ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + GNUNET_hash_to_enc (&mchk.query, &enc)); + GNUNET_GE_LOG (ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "Query for current block at level %u is `%s'.\n", i, + &enc); +#endif + if (GNUNET_OK != pushBlock (sock, + &mchk, i + 1, iblocks, priority, + expirationTime)) + { + GNUNET_GE_BREAK (ectx, 0); + goto FAILURE; + } + GNUNET_EC_file_block_encode (db, size, &mchk.query, &value); + if (value == NULL) + { + GNUNET_GE_BREAK (ectx, 0); + goto FAILURE; + } + value->expiration_time = GNUNET_htonll (expirationTime); + value->priority = htonl (priority); + if ((doIndex != GNUNET_SYSERR) && + (GNUNET_SYSERR == GNUNET_FS_insert (sock, value))) + { + GNUNET_GE_BREAK (ectx, 0); + GNUNET_free (value); + goto FAILURE; + } + GNUNET_free (value); + GNUNET_free (iblocks[i]); + iblocks[i] = NULL; + } +#if DEBUG_UPLOAD + IF_GELOG (ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + GNUNET_hash_to_enc (&mchk.query, &enc)); + GNUNET_GE_LOG (ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "Query for top block is %s\n", &enc); +#endif + /* build URI */ + fid.file_length = GNUNET_htonll (filesize); + db = (GNUNET_EC_DBlock *) & iblocks[treedepth][1]; + + fid.chk = *(GNUNET_EC_ContentHashKey *) & (db[1]); + *uri = GNUNET_malloc (sizeof (URI)); + (*uri)->type = chk; + (*uri)->data.fi = fid; + + /* free resources */ + GNUNET_free_non_null (iblocks[treedepth]); + GNUNET_free (iblocks); + GNUNET_free (dblock); + if (upcb != NULL) + upcb (filesize, filesize, eta, upcbClosure); + CLOSE (fd); + GNUNET_client_connection_destroy (sock); + return GNUNET_OK; +FAILURE: + for (i = 0; i <= treedepth; i++) + GNUNET_free_non_null (iblocks[i]); + GNUNET_free (iblocks); + GNUNET_free (dblock); + CLOSE (fd); + GNUNET_client_connection_destroy (sock); + return GNUNET_SYSERR; +} + +#endif + +/* end of fs_publish.c */ diff --git a/src/fs/fs_search.c b/src/fs/fs_search.c new file mode 100644 index 000000000..56240af69 --- /dev/null +++ b/src/fs/fs_search.c @@ -0,0 +1,572 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008 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/search.c + * @brief Helper functions for searching. + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_protocols.h" +#include "gnunet_fs_lib.h" +#include "gnunet_ecrs_lib.h" +#include "ecrs_core.h" +#include "ecrs.h" + +#define DEBUG_SEARCH GNUNET_NO + +/** + * Context for an individual search. Followed + * by keyCount keys of type GNUNET_HashCode. + */ +struct PendingSearch +{ + struct PendingSearch *next; + + struct GNUNET_ECRS_SearchContext *context; + + /** + * The key (for decryption) + */ + GNUNET_HashCode decryptKey; + + unsigned int keyCount; + + /** + * What type of query is it? + */ + unsigned int type; + +}; + +/** + * Context for search operation. + */ +struct GNUNET_ECRS_SearchContext +{ + /** + * Time when the cron-job was first started. + */ + GNUNET_CronTime start; + + /** + * What is the global timeout? + */ + GNUNET_CronTime timeout; + + /** + * Search context + */ + struct GNUNET_FS_SearchContext *sctx; + + /** + * Active searches. + */ + struct PendingSearch *queries; + + GNUNET_ECRS_SearchResultProcessor spcb; + + void *spcbClosure; + + struct GNUNET_GE_Context *ectx; + + struct GNUNET_GC_Configuration *cfg; + + int aborted; + + int my_sctx; + + unsigned int anonymityLevel; + +}; + +static int +receive_response_callback (const GNUNET_HashCode * key, + const GNUNET_DatastoreValue * value, + void *cls, unsigned long long uid); + +/** + * Add a query to the SQC. + */ +static void +add_search (unsigned int type, + unsigned int keyCount, + const GNUNET_HashCode * keys, + const GNUNET_HashCode * dkey, + struct GNUNET_ECRS_SearchContext *sqc) +{ + struct PendingSearch *ps; + + ps = + GNUNET_malloc (sizeof (struct PendingSearch) + + sizeof (GNUNET_HashCode) * keyCount); + ps->type = type; + ps->keyCount = keyCount; + memcpy (&ps[1], keys, sizeof (GNUNET_HashCode) * keyCount); + ps->decryptKey = *dkey; + ps->context = sqc; + ps->next = sqc->queries; + sqc->queries = ps; + GNUNET_FS_start_search (sqc->sctx, + NULL, + type, + keyCount, + keys, + sqc->anonymityLevel, + &receive_response_callback, ps); +} + +/** + * Add the query that corresponds to the given URI + * to the SQC. + */ +static void +add_search_for_uri (const struct GNUNET_ECRS_URI *uri, + struct GNUNET_ECRS_SearchContext *sqc) +{ + struct GNUNET_GE_Context *ectx = sqc->ectx; + + switch (uri->type) + { + case chk: + GNUNET_GE_LOG (ectx, + GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER, + _("CHK URI not allowed for search.\n")); + break; + case sks: + { + GNUNET_HashCode keys[2]; + GNUNET_HashCode hk; /* hk = GNUNET_hash(identifier) */ + GNUNET_HashCode hk2; /* hk2 = GNUNET_hash(hk) */ + + GNUNET_hash (uri->data.sks.identifier, + strlen (uri->data.sks.identifier), &hk); + GNUNET_hash (&hk, sizeof (GNUNET_HashCode), &hk2); + /* compute routing key keys[0] = H(key) ^ namespace */ + GNUNET_hash_xor (&hk2, &uri->data.sks.namespace, &keys[0]); + keys[1] = uri->data.sks.namespace; + add_search (GNUNET_ECRS_BLOCKTYPE_SIGNED, 2, &keys[0], &hk, sqc); + break; + } + case ksk: + { + GNUNET_HashCode hc; + GNUNET_HashCode query; + struct GNUNET_RSA_PrivateKey *pk; + GNUNET_RSA_PublicKey pub; + int i; + const char *keyword; + +#if DEBUG_SEARCH + GNUNET_GE_LOG (ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "Computing queries (this may take a while).\n"); +#endif + for (i = 0; i < uri->data.ksk.keywordCount; i++) + { + keyword = uri->data.ksk.keywords[i]; + /* first character of the keyword is + "+" or " " to indicate 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, &pub); + GNUNET_hash (&pub, sizeof (GNUNET_RSA_PublicKey), &query); + add_search (GNUNET_ECRS_BLOCKTYPE_ANY, /* GNUNET_ECRS_BLOCKTYPE_KEYWORD, GNUNET_ECRS_BLOCKTYPE_NAMESPACE or GNUNET_ECRS_BLOCKTYPE_KEYWORD_FOR_NAMESPACE ok */ + 1, &query, &hc, sqc); + GNUNET_RSA_free_key (pk); + } +#if DEBUG_SEARCH + GNUNET_GE_LOG (ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "Queries ready.\n"); +#endif + break; + } + case loc: + GNUNET_GE_LOG (ectx, + GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER, + _("LOC URI not allowed for search.\n")); + break; + default: + GNUNET_GE_BREAK (ectx, 0); + /* unknown URI type */ + break; + } +} + +/** + * We found an GNUNET_EC_SBlock. Decode the meta-data and call + * the callback of the SQC with the root-URI for the namespace, + * together with the namespace advertisement. Also, if this is + * a result with updates, automatically start the search for + * updates. + */ +static int +process_sblock_result (const GNUNET_EC_SBlock * sb, + const GNUNET_HashCode * key, + unsigned int size, + struct GNUNET_ECRS_SearchContext *sqc) +{ + static GNUNET_HashCode allZeros; + struct GNUNET_GE_Context *ectx = sqc->ectx; + GNUNET_ECRS_FileInfo fi; + URI updateURI; + int ret; + const char *id; + const char *uris; + unsigned int len; + unsigned int off; + int isRoot; + + len = size - sizeof (GNUNET_EC_SBlock); + off = GNUNET_string_buffer_tokenize ((const char *) &sb[1], + len, 2, &id, &uris); + if (off == 0) + { + GNUNET_GE_BREAK_OP (ectx, 0); /* sblock malformed */ + return GNUNET_SYSERR; + } + fi.meta = GNUNET_meta_data_deserialize (ectx, &id[off], len - off); + if (fi.meta == NULL) + { + GNUNET_GE_BREAK_OP (ectx, 0); /* sblock malformed */ + return GNUNET_SYSERR; + } + isRoot = 0 == memcmp (&sb->identifier, &allZeros, sizeof (GNUNET_HashCode)); + fi.uri = GNUNET_ECRS_string_to_uri (ectx, uris); + if ((isRoot) && (fi.uri == NULL)) + { + fi.uri = GNUNET_malloc (sizeof (URI)); + fi.uri->type = sks; + GNUNET_hash (&sb->subspace, + sizeof (GNUNET_RSA_PublicKey), + &fi.uri->data.sks.namespace); + fi.uri->data.sks.identifier = GNUNET_strdup (id); + } + if (fi.uri == NULL) + { + GNUNET_GE_BREAK_OP (ectx, 0); /* sblock malformed */ + GNUNET_meta_data_destroy (fi.meta); + return GNUNET_SYSERR; + } + if (sqc->spcb != NULL) + { + ret = sqc->spcb (&fi, key, isRoot, sqc->spcbClosure); + if (ret == GNUNET_SYSERR) + sqc->aborted = GNUNET_YES; + } + else + ret = GNUNET_OK; + if ((strlen (id) > 0) && (strlen (uris) > 0)) + { + updateURI.type = sks; + GNUNET_hash (&sb->subspace, + sizeof (GNUNET_RSA_PublicKey), + &updateURI.data.sks.namespace); + updateURI.data.sks.identifier = GNUNET_strdup (id); + add_search_for_uri (&updateURI, sqc); + GNUNET_free (updateURI.data.sks.identifier); + } + GNUNET_meta_data_destroy (fi.meta); + GNUNET_ECRS_uri_destroy (fi.uri); + return ret; +} + +/** + * Process replies received in response to our + * queries. Verifies, decrypts and passes valid + * replies to the callback. + * + * @return GNUNET_SYSERR if the entry is malformed + */ +static int +receive_response_callback (const GNUNET_HashCode * key, + const GNUNET_DatastoreValue * value, + void *cls, unsigned long long uid) +{ + struct PendingSearch *ps = cls; + struct GNUNET_ECRS_SearchContext *sqc = ps->context; + struct GNUNET_GE_Context *ectx = sqc->ectx; + unsigned int type; + GNUNET_ECRS_FileInfo fi; + unsigned int size; + int ret; + GNUNET_HashCode query; + GNUNET_CronTime expiration; + + expiration = GNUNET_ntohll (value->expiration_time); + if (expiration < GNUNET_get_time ()) + return GNUNET_OK; /* expired, ignore! */ + type = ntohl (value->type); + size = ntohl (value->size) - sizeof (GNUNET_DatastoreValue); +#if DEBUG_SEARCH + GNUNET_GE_LOG (ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "Search received reply of type %u and size %u.\n", type, + size); +#endif + if (GNUNET_OK != + GNUNET_EC_file_block_check_and_get_query (size, + (const GNUNET_EC_DBlock *) + &value[1], GNUNET_YES, + &query)) + { + GNUNET_GE_BREAK_OP (NULL, 0); + return GNUNET_SYSERR; + } + if (!((0 == memcmp (&query, + (GNUNET_HashCode *) & ps[1], sizeof (GNUNET_HashCode))) + && ((ps->type == type) || (ps->type == GNUNET_ECRS_BLOCKTYPE_ANY)) + && (GNUNET_YES == + GNUNET_EC_is_block_applicable_for_query (type, size, + (const GNUNET_EC_DBlock + *) &value[1], &query, + ps->keyCount, + (GNUNET_HashCode *) & + ps[1])))) + { + return GNUNET_OK; /* not a match */ + } + + switch (type) + { + case GNUNET_ECRS_BLOCKTYPE_KEYWORD: + { + GNUNET_EC_KBlock *kb; + const char *dstURI; +#if DEBUG_SEARCH + GNUNET_EncName enc; +#endif + int j; + + if (size < sizeof (GNUNET_EC_KBlock)) + { + GNUNET_GE_BREAK_OP (NULL, 0); + return GNUNET_SYSERR; + } + kb = GNUNET_malloc (size); + memcpy (kb, &value[1], size); +#if DEBUG_SEARCH + IF_GELOG (ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | + GNUNET_GE_USER, GNUNET_hash_to_enc (&ps->decryptKey, &enc)); + GNUNET_GE_LOG (ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | + GNUNET_GE_USER, + "Decrypting KBlock with key %s.\n", &enc); +#endif + GNUNET_ECRS_decryptInPlace (&ps->decryptKey, + &kb[1], size - sizeof (GNUNET_EC_KBlock)); + j = sizeof (GNUNET_EC_KBlock); + while ((j < size) && (((const char *) kb)[j] != '\0')) + j++; + if (j == size) + { + GNUNET_GE_BREAK_OP (ectx, 0); /* kblock malformed */ + GNUNET_free (kb); + return GNUNET_SYSERR; + } + dstURI = (const char *) &kb[1]; + j++; + fi.meta = GNUNET_meta_data_deserialize (ectx, + &((const char *) + kb)[j], size - j); + if (fi.meta == NULL) + { + GNUNET_GE_BREAK_OP (ectx, 0); /* kblock malformed */ + GNUNET_free (kb); + return GNUNET_SYSERR; + } + fi.uri = GNUNET_ECRS_string_to_uri (ectx, dstURI); + if (fi.uri == NULL) + { + GNUNET_GE_BREAK_OP (ectx, 0); /* kblock malformed */ + GNUNET_meta_data_destroy (fi.meta); + GNUNET_free (kb); + return GNUNET_SYSERR; + } + if (sqc->spcb != NULL) + { + ret = sqc->spcb (&fi, + &ps->decryptKey, GNUNET_NO, sqc->spcbClosure); + if (ret == GNUNET_SYSERR) + sqc->aborted = GNUNET_YES; + } + else + ret = GNUNET_OK; + GNUNET_ECRS_uri_destroy (fi.uri); + GNUNET_meta_data_destroy (fi.meta); + GNUNET_free (kb); + return ret; + } + case GNUNET_ECRS_BLOCKTYPE_SIGNED: + { + GNUNET_EC_SBlock *sb; + int ret; + + if (size < sizeof (GNUNET_EC_SBlock)) + { + GNUNET_GE_BREAK_OP (ectx, 0); /* sblock malformed */ + return GNUNET_SYSERR; + } + sb = GNUNET_malloc (size); + memcpy (sb, &value[1], size); + GNUNET_ECRS_decryptInPlace (&ps->decryptKey, + &sb[1], size - sizeof (GNUNET_EC_SBlock)); + ret = process_sblock_result (sb, &ps->decryptKey, size, sqc); + GNUNET_free (sb); + return ret; + } + case GNUNET_ECRS_BLOCKTYPE_KEYWORD_SIGNED: + { + GNUNET_EC_KSBlock *kb; + int ret; + + if (size < sizeof (GNUNET_EC_KSBlock)) + { + GNUNET_GE_BREAK_OP (ectx, 0); /* ksblock malformed */ + return GNUNET_SYSERR; + } + kb = GNUNET_malloc (size); + memcpy (kb, &value[1], size); + GNUNET_ECRS_decryptInPlace (&ps->decryptKey, + &kb->sblock, + size - sizeof (GNUNET_EC_KBlock) - + sizeof (unsigned int)); + ret = + process_sblock_result (&kb->sblock, &ps->decryptKey, + size - sizeof (GNUNET_EC_KSBlock) + + sizeof (GNUNET_EC_SBlock), sqc); + GNUNET_free (kb); + return ret; + } + default: + GNUNET_GE_BREAK_OP (ectx, 0); + break; + } /* end switch */ + return GNUNET_OK; +} + +/** + * Start search for content. + * + * @param uri specifies the search parameters + * @param uri set to the URI of the uploaded file + */ +struct GNUNET_ECRS_SearchContext * +GNUNET_ECRS_search_start (struct GNUNET_GE_Context *ectx, + struct GNUNET_GC_Configuration *cfg, + struct GNUNET_FS_SearchContext *sc, + const struct GNUNET_ECRS_URI *uri, + unsigned int anonymityLevel, + GNUNET_ECRS_SearchResultProcessor spcb, + void *spcbClosure) +{ + struct GNUNET_ECRS_SearchContext *ctx; + + if (GNUNET_YES == GNUNET_ECRS_uri_test_ksk (uri)) + { + if (1 != GNUNET_ECRS_uri_get_keyword_count_from_ksk (uri)) + return NULL; + } + else + { + if (GNUNET_YES != GNUNET_ECRS_uri_test_sks (uri)) + return NULL; + } + ctx = GNUNET_malloc (sizeof (struct GNUNET_ECRS_SearchContext)); + ctx->start = GNUNET_get_time (); + ctx->anonymityLevel = anonymityLevel; + ctx->ectx = ectx; + ctx->cfg = cfg; + ctx->queries = NULL; + ctx->spcb = spcb; + ctx->spcbClosure = spcbClosure; + ctx->aborted = GNUNET_NO; + ctx->sctx = sc == NULL ? GNUNET_FS_create_search_context (ectx, cfg) : sc; + if (ctx->sctx == NULL) + { + GNUNET_free (ctx); + return NULL; + } + ctx->my_sctx = (sc == NULL); + add_search_for_uri (uri, ctx); + return ctx; +} + +/** + * Stop search for content. + * + * @param uri specifies the search parameters + * @param uri set to the URI of the uploaded file + */ +void +GNUNET_ECRS_search_stop (struct GNUNET_ECRS_SearchContext *ctx) +{ + struct PendingSearch *pos; + + while (ctx->queries != NULL) + { + pos = ctx->queries; + ctx->queries = pos->next; + if (!ctx->my_sctx) + GNUNET_FS_stop_search (ctx->sctx, &receive_response_callback, pos); + GNUNET_free (pos); + } + if (ctx->my_sctx) + GNUNET_FS_destroy_search_context (ctx->sctx); + GNUNET_free (ctx); +} + +/** + * Search for content. + * + * @param timeout how long to wait (relative) + * @param uri specifies the search parameters + * @param uri set to the URI of the uploaded file + */ +int +GNUNET_ECRS_search (struct GNUNET_GE_Context *ectx, + struct GNUNET_GC_Configuration *cfg, + const struct GNUNET_ECRS_URI *uri, + unsigned int anonymityLevel, + GNUNET_ECRS_SearchResultProcessor spcb, + void *spcbClosure, GNUNET_ECRS_TestTerminate tt, + void *ttClosure) +{ + struct GNUNET_ECRS_SearchContext *ctx; + + ctx = + GNUNET_ECRS_search_start (ectx, cfg, NULL, + uri, anonymityLevel, spcb, spcbClosure); + if (ctx == NULL) + return GNUNET_SYSERR; + while (((NULL == tt) || (GNUNET_OK == tt (ttClosure))) + && (GNUNET_NO == GNUNET_shutdown_test ()) + && (ctx->aborted == GNUNET_NO)) + GNUNET_thread_sleep (100 * GNUNET_CRON_MILLISECONDS); + GNUNET_ECRS_search_stop (ctx); + return GNUNET_OK; +} + + +/* end of search.c */ diff --git a/src/fs/fs_test.c b/src/fs/fs_test.c new file mode 100644 index 000000000..5eedda982 --- /dev/null +++ b/src/fs/fs_test.c @@ -0,0 +1,259 @@ +/* + This file is part of GNUnet. + (C) 2004, 2005, 2006, 2008 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/fsui/basic_fsui_test.c + * @brief testcase for fsui (upload-search-download-unindex) + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_util.h" +#include "gnunet_fsui_lib.h" + +#define DEBUG_VERBOSE GNUNET_NO + +#define CHECK(a) if (!(a)) { ok = GNUNET_NO; GNUNET_GE_BREAK(NULL, 0); goto FAILURE; } + +static char * +makeName (unsigned int i) +{ + char *fn; + + fn = + GNUNET_malloc (strlen ("/tmp/gnunet-basic_fsui_test/BASIC_FSUI_TEST") + + 14); + GNUNET_snprintf (fn, + strlen ("/tmp/gnunet-basic_fsui_test/BASIC_FSUI_TEST") + + 14, "/tmp/gnunet-basic_fsui_test/BASIC_FSUI_TEST%u", i); + GNUNET_disk_directory_create_for_file (NULL, fn); + return fn; +} + +static volatile enum GNUNET_FSUI_EventType lastEvent; + +static struct GNUNET_MetaData *search_meta; + +static struct GNUNET_ECRS_URI *search_uri; + +static struct GNUNET_FSUI_Context *ctx; + +static void * +eventCallback (void *cls, const GNUNET_FSUI_Event * event) +{ + static char unused; + + switch (event->type) + { + case GNUNET_FSUI_search_resumed: + case GNUNET_FSUI_download_resumed: + case GNUNET_FSUI_upload_resumed: + case GNUNET_FSUI_unindex_resumed: + return &unused; + case GNUNET_FSUI_search_result: +#if DEBUG_VERBOSE + printf ("Received search result\n"); +#endif + search_uri = + GNUNET_ECRS_uri_duplicate (event->data.SearchResult.fi.uri); + search_meta = + GNUNET_meta_data_duplicate (event->data.SearchResult.fi.meta); + break; + case GNUNET_FSUI_upload_completed: +#if DEBUG_VERBOSE + printf ("Upload complete.\n"); +#endif + break; + case GNUNET_FSUI_download_completed: +#if DEBUG_VERBOSE + printf ("Download complete.\n"); +#endif + break; + case GNUNET_FSUI_unindex_completed: +#if DEBUG_VERBOSE + printf ("Unindex complete.\n"); +#endif + break; + default: + break; + } + lastEvent = event->type; + return NULL; +} + +#define START_DAEMON 1 + +int +main (int argc, char *argv[]) +{ +#if START_DAEMON + pid_t daemon; +#endif + int ok; + struct GNUNET_ECRS_URI *uri; + char *filename = NULL; + char *keywords[] = { + "fsui_foo", + "fsui_bar", + }; + char keyword[40]; + char *fn; + int prog; + struct GNUNET_MetaData *meta; + struct GNUNET_ECRS_URI *kuri; + struct GNUNET_GC_Configuration *cfg; + struct GNUNET_FSUI_UploadList *upload = NULL; + struct GNUNET_FSUI_SearchList *search = NULL; + struct GNUNET_FSUI_UnindexList *unindex = NULL; + struct GNUNET_FSUI_DownloadList *download = NULL; + + cfg = GNUNET_GC_create (); + if (-1 == GNUNET_GC_parse_configuration (cfg, "check.conf")) + { + GNUNET_GC_free (cfg); + return -1; + } +#if START_DAEMON + daemon = GNUNET_daemon_start (NULL, cfg, "peer.conf", GNUNET_NO); + GNUNET_GE_ASSERT (NULL, daemon > 0); + CHECK (GNUNET_OK == + GNUNET_wait_for_daemon_running (NULL, cfg, + 60 * GNUNET_CRON_SECONDS)); +#endif + GNUNET_thread_sleep (5 * GNUNET_CRON_SECONDS); /* give apps time to start */ + ok = GNUNET_YES; + + /* ACTUAL TEST CODE */ + ctx = GNUNET_FSUI_start (NULL, cfg, "basic_fsui_test", 32, /* thread pool size */ + GNUNET_NO, /* no resume */ + &eventCallback, NULL); + CHECK (ctx != NULL); + filename = makeName (42); + GNUNET_disk_file_write (NULL, + filename, + "foo bar test!", strlen ("foo bar test!"), "600"); + meta = GNUNET_meta_data_create (); + kuri = + GNUNET_ECRS_keyword_command_line_to_uri (NULL, 2, + (const char **) keywords); + /* upload */ + upload = GNUNET_FSUI_upload_start (ctx, filename, (GNUNET_FSUI_DirectoryScanCallback) & GNUNET_disk_directory_scan, NULL, 0, /* anonymity */ + 0, /* priority */ + GNUNET_YES, + GNUNET_NO, + GNUNET_NO, + GNUNET_get_time () + + 5 * GNUNET_CRON_HOURS, meta, kuri, kuri); + CHECK (upload != NULL); + GNUNET_ECRS_uri_destroy (kuri); + GNUNET_meta_data_destroy (meta); + prog = 0; + while (lastEvent != GNUNET_FSUI_upload_completed) + { + prog++; + CHECK (prog < + 10000) GNUNET_thread_sleep (50 * GNUNET_CRON_MILLISECONDS); + if (GNUNET_shutdown_test () == GNUNET_YES) + break; + } + + /* search */ + GNUNET_snprintf (keyword, 40, "+%s +%s", keywords[0], keywords[1]); + uri = GNUNET_ECRS_keyword_string_to_uri (NULL, keyword); + search = GNUNET_FSUI_search_start (ctx, 0, uri); + GNUNET_ECRS_uri_destroy (uri); + CHECK (search != NULL); + prog = 0; + while (lastEvent != GNUNET_FSUI_search_result) + { + prog++; + CHECK (prog < 10000); + GNUNET_thread_sleep (50 * GNUNET_CRON_MILLISECONDS); + if (GNUNET_shutdown_test () == GNUNET_YES) + break; + } + GNUNET_FSUI_search_abort (search); + GNUNET_FSUI_search_stop (search); + + /* download */ + fn = makeName (43); + download = GNUNET_FSUI_download_start (ctx, + 0, + GNUNET_NO, + search_uri, + search_meta, fn, NULL, NULL); + GNUNET_free (fn); + prog = 0; + while (lastEvent != GNUNET_FSUI_download_completed) + { + prog++; + CHECK (prog < 10000); + GNUNET_thread_sleep (50 * GNUNET_CRON_MILLISECONDS); + if (GNUNET_shutdown_test () == GNUNET_YES) + break; + } + GNUNET_FSUI_download_stop (download); + download = NULL; + GNUNET_ECRS_uri_destroy (search_uri); + GNUNET_meta_data_destroy (search_meta); + /* unindex */ + unindex = GNUNET_FSUI_unindex_start (ctx, filename); + prog = 0; + while (lastEvent != GNUNET_FSUI_unindex_completed) + { + prog++; + CHECK (prog < 10000); + GNUNET_thread_sleep (50 * GNUNET_CRON_MILLISECONDS); + if (GNUNET_shutdown_test () == GNUNET_YES) + break; + } + if (lastEvent != GNUNET_FSUI_unindex_completed) + GNUNET_FSUI_unindex_abort (unindex); + GNUNET_FSUI_unindex_stop (unindex); + + + /* END OF TEST CODE */ +FAILURE: + if (ctx != NULL) + GNUNET_FSUI_stop (ctx); + if (filename != NULL) + { + UNLINK (filename); + GNUNET_free (filename); + } + if (download != NULL) + { + GNUNET_FSUI_download_abort (download); + GNUNET_FSUI_download_stop (download); + } + filename = makeName (43); + /* TODO: verify file 'filename(42)' == file 'filename(43)' */ + UNLINK (filename); + GNUNET_free (filename); + +#if START_DAEMON + GNUNET_GE_ASSERT (NULL, GNUNET_OK == GNUNET_daemon_stop (NULL, daemon)); +#endif + GNUNET_GC_free (cfg); + + return (ok == GNUNET_YES) ? 0 : 1; +} + +/* end of basic_fsui_test.c */ diff --git a/src/fs/fs_unindex.c b/src/fs/fs_unindex.c new file mode 100644 index 000000000..ef63e798e --- /dev/null +++ b/src/fs/fs_unindex.c @@ -0,0 +1,394 @@ +/* + This file is part of GNUnet. + (C) 2003, 2004, 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/unindex.c + * @author Krista Bennett + * @author Christian Grothoff + * + * Unindex file. + * + * TODO: + * - code cleanup (share more with upload.c) + */ + +#include "platform.h" +#include "gnunet_protocols.h" +#include "gnunet_ecrs_lib.h" +#include "gnunet_fs_lib.h" +#include "gnunet_getoption_lib.h" +#include "ecrs_core.h" +#include "ecrs.h" +#include "fs.h" +#include "tree.h" + +#define STRICT_CHECKS GNUNET_NO + +/** + * Append the given key and query to the iblock[level]. + * If iblock[level] is already full, compute its chk + * and push it to level+1. iblocks is guaranteed to + * be big enough. + * + * This function matches exactly upload.c::pushBlock, + * except in the call to 'GNUNET_FS_delete'. TODO: refactor + * to avoid code duplication (move to block.c, pass + * GNUNET_FS_delete as argument!). + */ +static int +pushBlock (struct GNUNET_ClientServerConnection *sock, + const GNUNET_EC_ContentHashKey * chk, unsigned int level, + GNUNET_DatastoreValue ** iblocks) +{ + unsigned int size; + unsigned int present; + GNUNET_DatastoreValue *value; + GNUNET_EC_DBlock *db; + GNUNET_EC_ContentHashKey ichk; + + size = ntohl (iblocks[level]->size) - sizeof (GNUNET_DatastoreValue); + present = + (size - sizeof (GNUNET_EC_DBlock)) / sizeof (GNUNET_EC_ContentHashKey); + db = (GNUNET_EC_DBlock *) & iblocks[level][1]; + if (present == GNUNET_ECRS_CHK_PER_INODE) + { + GNUNET_EC_file_block_get_key (db, size, &ichk.key); + GNUNET_EC_file_block_get_query (db, size, &ichk.query); + if (GNUNET_OK != pushBlock (sock, &ichk, level + 1, iblocks)) + { + GNUNET_GE_BREAK (NULL, 0); + return GNUNET_SYSERR; + } + GNUNET_EC_file_block_encode (db, size, &ichk.query, &value); +#if STRICT_CHECKS + if (GNUNET_SYSERR == GNUNET_FS_delete (sock, value)) + { + GNUNET_free (value); + GNUNET_GE_BREAK (NULL, 0); + return GNUNET_SYSERR; + } +#else + GNUNET_FS_delete (sock, value); +#endif + GNUNET_free (value); + size = sizeof (GNUNET_EC_DBlock); + } + /* append GNUNET_EC_ContentHashKey */ + memcpy (&((char *) db)[size], chk, sizeof (GNUNET_EC_ContentHashKey)); + iblocks[level]->size = htonl (size + + sizeof (GNUNET_EC_ContentHashKey) + + sizeof (GNUNET_DatastoreValue)); + return GNUNET_OK; +} + + + +/** + * Undo sym-linking operation: + * a) check if we have a symlink + * b) delete symbolic link + */ +static int +undoSymlinking (struct GNUNET_GE_Context *ectx, + const char *fn, + const GNUNET_HashCode * fileId, + struct GNUNET_ClientServerConnection *sock) +{ + GNUNET_EncName enc; + char *serverDir; + char *serverFN; + struct stat buf; + +#ifndef S_ISLNK + if (1) + return GNUNET_OK; /* symlinks do not exist? */ +#endif + if (0 != LSTAT (fn, &buf)) + { + GNUNET_GE_LOG_STRERROR_FILE (ectx, + GNUNET_GE_ERROR | GNUNET_GE_BULK | + GNUNET_GE_USER | GNUNET_GE_ADMIN, "stat", + fn); + return GNUNET_SYSERR; + } +#ifdef S_ISLNK + if (!S_ISLNK (buf.st_mode)) + return GNUNET_OK; +#endif + serverDir = + GNUNET_get_daemon_configuration_value (sock, "FS", "INDEX-DIRECTORY"); + if (serverDir == NULL) + return GNUNET_OK; + serverFN = GNUNET_malloc (strlen (serverDir) + 2 + sizeof (GNUNET_EncName)); + strcpy (serverFN, serverDir); + GNUNET_free (serverDir); + if (serverFN[strlen (serverFN) - 1] != DIR_SEPARATOR) + strcat (serverFN, DIR_SEPARATOR_STR); + GNUNET_hash_to_enc (fileId, &enc); + strcat (serverFN, (char *) &enc); + + if (0 != UNLINK (serverFN)) + { + GNUNET_GE_LOG_STRERROR_FILE (ectx, + GNUNET_GE_ERROR | GNUNET_GE_BULK | + GNUNET_GE_USER | GNUNET_GE_ADMIN, "unlink", + serverFN); + GNUNET_free (serverFN); + return GNUNET_SYSERR; + } + GNUNET_free (serverFN); + return GNUNET_OK; +} + + + +/** + * Unindex a file. + * + * @return GNUNET_SYSERR if the unindexing failed (i.e. not indexed) + */ +int +GNUNET_ECRS_file_unindex (struct GNUNET_GE_Context *ectx, + struct GNUNET_GC_Configuration *cfg, + const char *filename, + GNUNET_ECRS_UploadProgressCallback upcb, + void *upcbClosure, GNUNET_ECRS_TestTerminate tt, + void *ttClosure) +{ + unsigned long long filesize; + unsigned long long pos; + unsigned int treedepth; + int fd; + int i; + unsigned int size; + GNUNET_DatastoreValue **iblocks; + GNUNET_DatastoreValue *dblock; + GNUNET_EC_DBlock *db; + GNUNET_DatastoreValue *value; + struct GNUNET_ClientServerConnection *sock; + GNUNET_HashCode fileId; + GNUNET_EC_ContentHashKey chk; + GNUNET_CronTime eta; + GNUNET_CronTime start; + GNUNET_CronTime now; + int wasIndexed; + + start = GNUNET_get_time (); + if (GNUNET_YES != GNUNET_disk_file_test (ectx, filename)) + { + GNUNET_GE_BREAK (ectx, 0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_disk_file_size (ectx, filename, &filesize, GNUNET_YES)) + return GNUNET_SYSERR; + sock = GNUNET_client_connection_create (ectx, cfg); + if (sock == NULL) + return GNUNET_SYSERR; + eta = 0; + if (upcb != NULL) + upcb (filesize, 0, eta, upcbClosure); + if (GNUNET_SYSERR == GNUNET_hash_file (ectx, filename, &fileId)) + { + GNUNET_client_connection_destroy (sock); + GNUNET_GE_BREAK (ectx, 0); + return GNUNET_SYSERR; + } + now = GNUNET_get_time (); + eta = now + 2 * (now - start); + /* very rough estimate: GNUNET_hash reads once through the file, + we'll do that once more and write it. But of course + the second read may be cached, and we have the encryption, + so a factor of two is really, really just a rough estimate */ + start = now; + /* reset the counter since the formula later does not + take the time for GNUNET_hash_file into account */ + treedepth = GNUNET_ECRS_compute_depth (filesize); + + /* Test if file is indexed! */ + wasIndexed = GNUNET_FS_test_indexed (sock, &fileId); + + fd = GNUNET_disk_file_open (ectx, filename, O_RDONLY | O_LARGEFILE); + if (fd == -1) + { + GNUNET_client_connection_destroy (sock); + return GNUNET_SYSERR; + } + dblock = + GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + GNUNET_ECRS_DBLOCK_SIZE + + sizeof (GNUNET_EC_DBlock)); + dblock->size = + htonl (sizeof (GNUNET_DatastoreValue) + GNUNET_ECRS_DBLOCK_SIZE + + sizeof (GNUNET_EC_DBlock)); + dblock->anonymity_level = htonl (0); + dblock->priority = htonl (0); + dblock->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA); + dblock->expiration_time = GNUNET_htonll (0); + db = (GNUNET_EC_DBlock *) & dblock[1]; + db->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA); + iblocks = + GNUNET_malloc (sizeof (GNUNET_DatastoreValue *) * (treedepth + 1)); + for (i = 0; i <= treedepth; i++) + { + iblocks[i] = + GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + + GNUNET_ECRS_IBLOCK_SIZE + sizeof (GNUNET_EC_DBlock)); + iblocks[i]->size = + htonl (sizeof (GNUNET_DatastoreValue) + sizeof (GNUNET_EC_DBlock)); + iblocks[i]->anonymity_level = htonl (0); + iblocks[i]->priority = htonl (0); + iblocks[i]->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA); + iblocks[i]->expiration_time = GNUNET_htonll (0); + ((GNUNET_EC_DBlock *) & iblocks[i][1])->type = + htonl (GNUNET_ECRS_BLOCKTYPE_DATA); + } + + pos = 0; + while (pos < filesize) + { + if (upcb != NULL) + upcb (filesize, pos, eta, upcbClosure); + if (tt != NULL) + if (GNUNET_OK != tt (ttClosure)) + goto FAILURE; + size = GNUNET_ECRS_DBLOCK_SIZE; + if (size > filesize - pos) + { + size = filesize - pos; + memset (&db[1], 0, GNUNET_ECRS_DBLOCK_SIZE); + } + dblock->size = + htonl (sizeof (GNUNET_DatastoreValue) + size + + sizeof (GNUNET_EC_DBlock)); + if (size != READ (fd, &db[1], size)) + { + GNUNET_GE_LOG_STRERROR_FILE (ectx, + GNUNET_GE_ERROR | GNUNET_GE_USER | + GNUNET_GE_ADMIN | GNUNET_GE_BULK, + "READ", filename); + goto FAILURE; + } + if (tt != NULL) + if (GNUNET_OK != tt (ttClosure)) + goto FAILURE; + GNUNET_EC_file_block_get_key (db, size + sizeof (GNUNET_EC_DBlock), + &chk.key); + GNUNET_EC_file_block_get_query (db, size + sizeof (GNUNET_EC_DBlock), + &chk.query); + if (GNUNET_OK != pushBlock (sock, &chk, 0, /* dblocks are on level 0 */ + iblocks)) + { + GNUNET_GE_BREAK (ectx, 0); + goto FAILURE; + } + if (!wasIndexed) + { + if (GNUNET_OK == + GNUNET_EC_file_block_encode (db, size, &chk.query, &value)) + { + *value = *dblock; /* copy options! */ +#if STRICT_CHECKS + if (GNUNET_OK != GNUNET_FS_delete (sock, value)) + { + GNUNET_free (value); + GNUNET_GE_BREAK (ectx, 0); + goto FAILURE; + } +#else + GNUNET_FS_delete (sock, value); +#endif + GNUNET_free (value); + } + else + { + goto FAILURE; + } + } + pos += size; + now = GNUNET_get_time (); + eta = (GNUNET_CronTime) (start + + (((double) (now - start) / (double) pos)) + * (double) filesize); + } + if (tt != NULL) + if (GNUNET_OK != tt (ttClosure)) + goto FAILURE; + for (i = 0; i < treedepth; i++) + { + size = ntohl (iblocks[i]->size) - sizeof (GNUNET_DatastoreValue); + db = (GNUNET_EC_DBlock *) & iblocks[i][1]; + GNUNET_EC_file_block_get_key (db, size, &chk.key); + GNUNET_EC_file_block_get_query (db, size, &chk.query); + if (GNUNET_OK != pushBlock (sock, &chk, i + 1, iblocks)) + { + GNUNET_GE_BREAK (ectx, 0); + goto FAILURE; + } + GNUNET_EC_file_block_encode (db, size, &chk.query, &value); +#if STRICT_CHECKS + if (GNUNET_OK != GNUNET_FS_delete (sock, value)) + { + GNUNET_free (value); + GNUNET_GE_BREAK (ectx, 0); + goto FAILURE; + } +#else + GNUNET_FS_delete (sock, value); +#endif + GNUNET_free (value); + GNUNET_free (iblocks[i]); + iblocks[i] = NULL; + } + + if (wasIndexed) + { + if (GNUNET_OK == undoSymlinking (ectx, filename, &fileId, sock)) + { + if (GNUNET_OK != + GNUNET_FS_unindex (sock, GNUNET_ECRS_DBLOCK_SIZE, &fileId)) + { + GNUNET_GE_BREAK (ectx, 0); + goto FAILURE; + } + } + else + { + GNUNET_GE_BREAK (ectx, 0); + goto FAILURE; + } + } + GNUNET_free (iblocks[treedepth]); + /* free resources */ + GNUNET_free (iblocks); + GNUNET_free (dblock); + CLOSE (fd); + GNUNET_client_connection_destroy (sock); + return GNUNET_OK; +FAILURE: + for (i = 0; i <= treedepth; i++) + GNUNET_free_non_null (iblocks[i]); + GNUNET_free (iblocks); + GNUNET_free (dblock); + CLOSE (fd); + GNUNET_client_connection_destroy (sock); + return GNUNET_SYSERR; +} + +/* end of unindex.c */ diff --git a/src/fs/fs_uri.c b/src/fs/fs_uri.c index f2351af1b..8e69a8425 100644 --- a/src/fs/fs_uri.c +++ b/src/fs/fs_uri.c @@ -26,10 +26,10 @@ * GNUnet URIs are of the general form "gnunet://MODULE/IDENTIFIER". * The specific structure of "IDENTIFIER" depends on the module and * maybe differenciated into additional subcategories if applicable. - * This module only deals with ecrs identifiers (MODULE = "ecrs"). + * This module only deals with fs identifiers (MODULE = "fs"). *

* - * This module only parses URIs for the AFS module. The ECRS URIs fall + * This module only parses URIs for the AFS module. The FS URIs fall * into four categories, "chk", "sks", "ksk" and "loc". The first three * categories were named in analogy (!) to Freenet, but they do NOT * work in exactly the same way. They are very similar from the user's @@ -40,7 +40,7 @@ *