diff options
Diffstat (limited to 'src/fs/fs_publish.c')
-rw-r--r-- | src/fs/fs_publish.c | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/src/fs/fs_publish.c b/src/fs/fs_publish.c new file mode 100644 index 0000000000..ac71c8f21f --- /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 */ |