diff --git a/blocks/block.c b/blocks/block.c new file mode 100644 index 0000000..60401cf --- /dev/null +++ b/blocks/block.c @@ -0,0 +1,40 @@ +/** + * The implementation of methods around IPFS blocks + */ + +#include + +#include "ipfs/blocks/block.h" +#include "ipfs/cid/cid.h" + +/*** + * Create a new block based on the incoming data + * @param data the data to base the block on + * @param data_size the length of the data array + * @param block a pointer to the struct Block that will be created + * @returns true(1) on success + */ +int ipfs_blocks_block_new(unsigned char* data, size_t data_size, struct Block* block) { + char hash[32]; + ipfs_crypto_hashing_sha256(data, hash, 32); + ipfs_cid_new(0, hash, 32, CID_PROTOBUF, block->cid); + + block->data = malloc(sizeof(unsigned char) * data_size); + if (block->data == NULL) + return 0; + + memcpy(block->data, data, data_size); + return 1; +} + +/*** + * Free resources used by the creation of a block + * @param block the block to free + * @returns true(1) on success + */ +int ipfs_blocks_block_free(struct Block* block) { + ipfs_cid_free(block->cid); + if (block->data != NULL) + free(block->data); + return 1; +} diff --git a/blocks/blockstore.c b/blocks/blockstore.c new file mode 100644 index 0000000..c4863f8 --- /dev/null +++ b/blocks/blockstore.c @@ -0,0 +1,41 @@ +/*** + * a thin wrapper over a datastore for getting and putting block objects + */ + + +/** + * Delete a block based on its Cid + * @param cid the Cid to look for + * @param returns true(1) on success + */ +int ipfs_blockstore_delete(struct Cid* cid) { + return 0; +} + +/*** + * Determine if the Cid can be found + * @param cid the Cid to look for + * @returns true(1) if found + */ +int ipfs_blockstore_has(struct Cid* cid) { + return 0; +} + +/*** + * Find a block based on its Cid + * @param cid the Cid to look for + * @param block where to put the data to be returned + * @returns true(1) on success + */ +int ipfs_blockstore_get(struct Cid* cid, struct Block* block) { + return 0; +} + +/*** + * Put a block in the blockstore + * @param block the block to store + * @returns true(1) on success + */ +int ipfs_blockstore_put(struct Block* block) { + return 0; +} diff --git a/cmd/ipfs/init.c b/cmd/ipfs/init.c index 38670ac..6b89487 100644 --- a/cmd/ipfs/init.c +++ b/cmd/ipfs/init.c @@ -24,14 +24,14 @@ int init_pre_run(struct Request* request) { /** * This actually opens the repo and gets things set up - * @param repo_root the root of the repository + * @param repo the repo information * @returns true(1) on success */ -int initialize_ipns_keyspace(char* repo_root) { - //TODO: open fs repo - struct FSRepo repo; - // get the path - int retVal = fs_repo_open(repo_root, &repo); +int initialize_ipns_keyspace(struct FSRepo* repo) { + //open fs repo + int retVal = ipfs_repo_fsrepo_open(repo); + if (retVal == 0) + return 0; //TODO: make a new node, then close it //TODO: setup offline routing on new node struct IpfsNode* ipfs_node; @@ -66,11 +66,16 @@ int do_init(FILE* out_file, char* repo_root, int empty, int num_bits_for_keypair return 0; } // initialize the fs repo - int retVal = fs_repo_init(repo_root, conf); + struct FSRepo* repo; + int retVal = ipfs_repo_fsrepo_new(repo_root, conf, &repo); if (retVal == 0) return 0; + retVal = ipfs_repo_fsrepo_init(repo); + if (retVal == 0) + return 0; + //TODO: add default assets - return initialize_ipns_keyspace(repo_root); + return initialize_ipns_keyspace(repo); } /*** diff --git a/include/ipfs/blocks/block.h b/include/ipfs/blocks/block.h new file mode 100644 index 0000000..28802c2 --- /dev/null +++ b/include/ipfs/blocks/block.h @@ -0,0 +1,29 @@ +/*** + * IPFS has the notion of storage blocks. + */ + +#ifndef __IPFS_BLOCKS_BLOCK_H__ +#define __IPFS_BLOCKS_BLOCK_H__ + +struct Block { + struct Cid* cid; + unsigned char* data; +}; + +/*** + * Create a new block based on the incoming data. + * @param data the data to base the block on + * @param data_size the length of the data array + * @param block a pointer to the struct Block that will be created + * @returns true(1) on success + */ +int ipfs_blocks_block_new(unsigned char* data, size_t data_size, struct Block* block); + +/*** + * Free resources used by the creation of a block + * @param block the block to free + * @returns true(1) on success + */ +int ipfs_blocks_block_free(struct Block* block); + +#endif diff --git a/include/ipfs/blocks/blockstore.h b/include/ipfs/blocks/blockstore.h new file mode 100644 index 0000000..a4e10d6 --- /dev/null +++ b/include/ipfs/blocks/blockstore.h @@ -0,0 +1,38 @@ +/*** + * a thin wrapper over a datastore for getting and putting block objects + */ + +#ifndef __IPFS_BLOCKS_BLOCKSTORE_H__ +#ifndef __IPFS_BLOCKS_BLOCKSTORE_H__ + +/** + * Delete a block based on its Cid + * @param cid the Cid to look for + * @param returns true(1) on success + */ +int ipfs_blockstore_delete(struct Cid* cid); + +/*** + * Determine if the Cid can be found + * @param cid the Cid to look for + * @returns true(1) if found + */ +int ipfs_blockstore_has(struct Cid* cid); + +/*** + * Find a block based on its Cid + * @param cid the Cid to look for + * @param block where to put the data to be returned + * @returns true(1) on success + */ +int ipfs_blockstore_get(struct Cid* cid, struct Block* block); + +/*** + * Put a block in the blockstore + * @param block the block to store + * @returns true(1) on success + */ +int ipfs_blockstore_put(struct Block* block); + + +#endif diff --git a/include/ipfs/os/utils.h b/include/ipfs/os/utils.h index c440fc6..a01dc04 100644 --- a/include/ipfs/os/utils.h +++ b/include/ipfs/os/utils.h @@ -13,7 +13,7 @@ * @param variable the variable to look for * @returns the results */ -char* os_utils_getenv(char* variable); +char* os_utils_getenv(const char* variable); /** * get the user's home directory @@ -28,10 +28,12 @@ char* os_utils_get_homedir(); * @param results where to put the results * @param max_len throw an error if the total is longer than max_len */ -int os_utils_filepath_join(char* root, char* extension, char* results, unsigned long max_len); +int os_utils_filepath_join(const char* root, const char* extension, char* results, unsigned long max_len); -int os_utils_file_exists(char* file_name); +int os_utils_file_exists(const char* file_name); -int os_utils_directory_writeable(char* path); +int os_utils_file_size(const char* file_name); + +int os_utils_directory_writeable(const char* path); #endif /* utils_h */ diff --git a/include/ipfs/repo/config/config.h b/include/ipfs/repo/config/config.h index 3d959e7..2dcd4f0 100644 --- a/include/ipfs/repo/config/config.h +++ b/include/ipfs/repo/config/config.h @@ -74,11 +74,18 @@ int config_path(char* config_root, char* extension, char* result, int max_len); */ int repo_config_init(struct RepoConfig* config, unsigned int num_bits_for_keypair, char* repo_path); +/*** + * Initialize memory for a RepoConfig struct + * @param config the structure to initialize + * @returns true(1) on success + */ +int ipfs_repo_config_new(struct RepoConfig** config); + /*** * free all resources that were allocated to store config information * @param config the config * @returns true(1) */ -int repo_config_free(struct RepoConfig* config); +int ipfs_repo_config_free(struct RepoConfig* config); #endif diff --git a/include/ipfs/repo/config/datastore.h b/include/ipfs/repo/config/datastore.h index c075265..40bd94f 100644 --- a/include/ipfs/repo/config/datastore.h +++ b/include/ipfs/repo/config/datastore.h @@ -11,27 +11,41 @@ struct Datastore { char* storage_max; int storage_gc_watermark; char* gc_period; - char* params; int no_sync; int hash_on_read; int bloom_filter_size; + + // function pointers for datastore operations + int (*datastore_open)(int argc, char** argv, struct Datastore* datastore); + int (*datastore_close)(int argc, char** argv, struct Datastore* datastore); + // a handle to the datastore "context" used by the datastore + void* handle; }; /*** - * initialize the structure of the datastore + * Initialize the structure of the datastore to default settings. Used for + * creating a new datastore on the disk. * @param datastore the struct to initialize * @param config_root the path to the root of IPFS * @returns true(1) on success */ -int repo_config_datastore_init(struct Datastore* datastore, char* config_root); +int ipfs_repo_config_datastore_init(struct Datastore* datastore, char* config_root); + +/*** + * initialize the structure of the datastore + * @param datastore the struct to initialize + * @returns true(1) on success + */ +int ipfs_repo_config_datastore_new(struct Datastore** datastore); + /*** * deallocate the memory and clear resources from a datastore_init * @param datastore the struct to deallocate * @returns true(1) */ -int repo_config_datastore_free(struct Datastore* datastore); +int ipfs_repo_config_datastore_free(struct Datastore* datastore); #endif diff --git a/include/ipfs/repo/config/identity.h b/include/ipfs/repo/config/identity.h index 7f594b8..c1831c4 100644 --- a/include/ipfs/repo/config/identity.h +++ b/include/ipfs/repo/config/identity.h @@ -19,7 +19,15 @@ struct Identity { /*** * initializes a new keypair, and puts it in the Identity struct */ -int repo_config_identity_new(struct Identity* identity, unsigned long num_bits_for_keypair); +int repo_config_identity_init(struct Identity* identity, unsigned long num_bits_for_keypair); + +/*** + * Build a RsaPrivateKey struct from a base64 string of the private key + * @param identity where to put the new struct + * @param base64 the base 64 encoded private key in DER format + * @returns true(1) on success + */ +int repo_config_identity_build_private_key(struct Identity* identity, const char* base64); /*** * Frees resources held by Identity @@ -28,4 +36,11 @@ int repo_config_identity_new(struct Identity* identity, unsigned long num_bits_f */ int repo_config_identity_free(struct Identity* identity); +/*** + * Initializes a new identity struct that will need to be identity_free'd + * @param identity the identity to allocate memory for + * @returns true(1) on success + */ +int repo_config_identity_new(struct Identity** identity); + #endif /* identity_h */ diff --git a/include/ipfs/repo/fsrepo/fs_repo.h b/include/ipfs/repo/fsrepo/fs_repo.h index 43f82f0..1b93b25 100644 --- a/include/ipfs/repo/fsrepo/fs_repo.h +++ b/include/ipfs/repo/fsrepo/fs_repo.h @@ -15,7 +15,7 @@ struct FSRepo { int closed; char* path; struct IOCloser* lock_file; - struct Config* config; + struct RepoConfig* config; struct Datastore* data_store; }; @@ -25,7 +25,7 @@ struct FSRepo { * @param repo where to store the repo info * @return 0 if there was a problem, otherwise 1 */ -int fs_repo_open(char* repo_path, struct FSRepo* repo); +int ipfs_repo_fsrepo_open(struct FSRepo* repo); /*** * checks to see if the repo is initialized @@ -48,7 +48,20 @@ int fs_repo_write_config_file(char* path, struct RepoConfig* config); * @param config the information for the config file * @returns true(1) on success */ -int fs_repo_init(char* repo_path, struct RepoConfig* config); +int ipfs_repo_fsrepo_new(char* repo_path, struct RepoConfig* config, struct FSRepo** fs_repo); +/*** + * Free all resources used by this struct + * @param repo the repo to clean up + * @returns true(1) on success + */ +int ipfs_repo_fsrepo_free(struct FSRepo* config); + +/*** + * Initialize a new repo with the specified configuration + * @param config the information in order to build the repo + * @returns true(1) on success + */ +int ipfs_repo_fsrepo_init(struct FSRepo* config); #endif /* fs_repo_h */ diff --git a/include/ipfs/repo/fsrepo/lmdb_datastore.h b/include/ipfs/repo/fsrepo/lmdb_datastore.h new file mode 100644 index 0000000..38920b0 --- /dev/null +++ b/include/ipfs/repo/fsrepo/lmdb_datastore.h @@ -0,0 +1,30 @@ +#ifndef __FS_REPO_LMDB_DATASTORE_H__ +#define __FS_REPO_LMDB_DATASTORE_H__ + +#include "ipfs/repo/config/datastore.h" + +/*** + * Places the LMDB methods into the datastore's function pointers + * @param datastore the datastore to fill + * @returns true(1) on success; + */ +int repo_fsrepo_lmdb_cast(struct Datastore* datastore); + +/** + * Open an lmdb database with the given parameters. + * Note: for now, the parameters are not used + * @param argc number of parameters in the following array + * @param argv an array of parameters + */ +int repo_fsrepro_lmdb_open(int argc, char** argv, struct Datastore* datastore); + +/*** + * Close an LMDB database + * NOTE: for now, argc and argv are not used + * @param argc number of parameters in the argv array + * @param argv parameters to be passed in + * @param datastore the datastore struct that contains information about the opened database + */ +int repo_fsrepo_lmdb_close(int argc, char** argv, struct Datastore* datastore); + +#endif diff --git a/os/utils.c b/os/utils.c index 9416d68..9fbb36b 100644 --- a/os/utils.c +++ b/os/utils.c @@ -4,13 +4,14 @@ #include #include #include +#include /** * get an environment varible from the os * @param variable the variable to look for * @returns the results */ -char* os_utils_getenv(char* variable) { +char* os_utils_getenv(const char* variable) { return getenv(variable); } @@ -30,22 +31,36 @@ char* os_utils_get_homedir() { * @param results where to put the results * @param max_len throw an error if the total is longer than max_len */ -int os_utils_filepath_join(char* root, char* extension, char* results, unsigned long max_len) { - //TODO: implement slash checks +int os_utils_filepath_join(const char* root, const char* extension, char* results, unsigned long max_len) { if (strlen(root) + strlen(extension) + 1 > max_len) return 0; strncpy(results, root, strlen(root) + 1); + // one of these should have a slash. If not, add one + if (root[strlen(root)-1] != '/' && extension[0] != '/') { + results[strlen(root)] = '/'; + results[strlen(root)+1] = 0; + } strncat(results, extension, strlen(extension)+1); return 1; } -int os_utils_file_exists(char* file_name) { +int os_utils_file_exists(const char* file_name) { if (access(file_name, F_OK) != -1) return 1; return 0; } -int os_utils_directory_writeable(char* path) { +int os_utils_directory_writeable(const char* path) { int result = access(path, W_OK); return result == 0; } + +int os_utils_file_size(const char* path) { + // open file + FILE* in_file = fopen(path, "r"); + // determine size + fseek(in_file, 0L, SEEK_END); + size_t file_size = ftell(in_file); + fclose(in_file); + return file_size; +} diff --git a/repo/config/Makefile b/repo/config/Makefile index c8f8e71..a25401c 100644 --- a/repo/config/Makefile +++ b/repo/config/Makefile @@ -1,5 +1,5 @@ CC = gcc -CFLAGS = -O0 -I../../include -I../../../c-libp2p/include +CFLAGS = -O0 -I../../include -I../../../c-libp2p/include -I../../../c-multihash/include ifdef DEBUG CFLAGS += -g3 diff --git a/repo/config/config.c b/repo/config/config.c index 6919603..7dc65e6 100644 --- a/repo/config/config.c +++ b/repo/config/config.c @@ -78,7 +78,7 @@ int repo_config_get_file_name(char* path, char** result) { if (result == NULL) return 0; - return os_utils_filepath_join(path, "/config", *result, max_len); + return os_utils_filepath_join(path, "config", *result, max_len); } /*** @@ -88,9 +88,8 @@ int repo_config_get_file_name(char* path, char** result) { * @returns true(1) on success, otherwise 0 */ int repo_config_init(struct RepoConfig* config, unsigned int num_bits_for_keypair, char* repo_path) { - // identity - int retVal = repo_config_identity_new(&(config->identity), num_bits_for_keypair); + int retVal = repo_config_identity_init(&(config->identity), num_bits_for_keypair); if (retVal == 0) return 0; @@ -100,7 +99,7 @@ int repo_config_init(struct RepoConfig* config, unsigned int num_bits_for_keypai return 0; // datastore - retVal = repo_config_datastore_init(&(config->datastore), repo_path); + retVal = ipfs_repo_config_datastore_init(&(config->datastore), repo_path); if (retVal == 0) return 0; @@ -139,10 +138,30 @@ int repo_config_init(struct RepoConfig* config, unsigned int num_bits_for_keypai return 1; } -int repo_config_free(struct RepoConfig* config) { - repo_config_bootstrap_peers_free(&(config->peer_addresses)); - repo_config_datastore_free(&(config->datastore)); - repo_config_addresses_free(&(config->addresses)); +/*** + * Initialize memory for a RepoConfig struct + * @param config the structure to initialize + * @returns true(1) on success + */ +int ipfs_repo_config_new(struct RepoConfig** config) { + *config = (struct RepoConfig*)malloc(sizeof(struct RepoConfig)); + if (*config == NULL) + return 0; + return 1; +} + +/** + * Free resources + * @param config the struct to be freed + * @returns true(1) on success + */ +int ipfs_repo_config_free(struct RepoConfig* config) { + if (config != NULL) { + repo_config_bootstrap_peers_free(&(config->peer_addresses)); + //ipfs_repo_config_datastore_free(&(config->datastore)); + repo_config_addresses_free(&(config->addresses)); + free(config); + } return 1; } diff --git a/repo/config/datastore.c b/repo/config/datastore.c index a0abe6c..eaa874e 100644 --- a/repo/config/datastore.c +++ b/repo/config/datastore.c @@ -17,11 +17,11 @@ * @param datastore the struct to initialize * @returns true(1) on success */ -int repo_config_datastore_init(struct Datastore* datastore, char* config_root) { +int ipfs_repo_config_datastore_init(struct Datastore* datastore, char* config_root) { unsigned long stringLength = strlen(config_root) + 12; datastore->path = malloc(sizeof(char) * stringLength); - os_utils_filepath_join(config_root, "/datastore", datastore->path, stringLength); - datastore->type = "leveldb"; + os_utils_filepath_join(config_root, "datastore", datastore->path, stringLength); + datastore->type = "lmdb"; datastore->storage_max = "10GB"; datastore->storage_gc_watermark = 90; datastore->gc_period = "1h"; @@ -30,12 +30,30 @@ int repo_config_datastore_init(struct Datastore* datastore, char* config_root) { return 1; } +/*** + * initialize the structure of the datastore + * @param datastore the struct to initialize + * @returns true(1) on success + */ +int ipfs_repo_config_datastore_new(struct Datastore** datastore) { + *datastore = malloc(sizeof(struct Datastore)); + if (*datastore == NULL) + return 0; + (*datastore)->path = NULL; + return 1; +} + /*** * deallocate the memory and clear resources from a datastore_init * @param datastore the struct to deallocate * @returns true(1) */ -int repo_config_datastore_free(struct Datastore* datastore) { - //free(datastore); +int ipfs_repo_config_datastore_free(struct Datastore* datastore) { + if (datastore != NULL) + { + if (datastore->path != NULL) + free(datastore->path); + free(datastore); + } return 1; } diff --git a/repo/config/identity.c b/repo/config/identity.c index d0ca8de..c109480 100644 --- a/repo/config/identity.c +++ b/repo/config/identity.c @@ -8,36 +8,104 @@ #include "ipfs/repo/config/identity.h" #include "libp2p/crypto/rsa.h" +#include "libp2p/peerutils.h" +#include "libp2p/crypto/encoding/base64.h" +#include "libp2p/crypto/encoding/x509.h" + +/** + * Builds the Peer ID using the private key, and places it in the identity->peer_id + * @param identity Where to get the DER of the private key + * @returns true(1) on success + */ +int repo_config_identity_build_peer_id(struct Identity* identity) { + // ic key and PeerID + char hash[32]; + ID_FromPK_non_null_terminated(hash, identity->private_key.der, identity->private_key.der_length); + + // peer id is multihashed + size_t sz = 255; + char results[sz]; + if (PrettyID(results, &sz, hash, 32) == 0) + return 0; + + // copy it into the structure + identity->peer_id = (char*)malloc(sz + 1); + if (identity->peer_id == NULL) + return 0; + + strncpy(identity->peer_id, results, sz); + identity->peer_id[sz] = 0; + return 1; +} /*** * public methods */ /*** - * initializes a new Identity + * Initializes a new Identity. NOTE: This builds a new private/public key pair * @param identity the identity to fill * @param num_bits_for_keypair the number of bits for the keypair * @returns true(1) on success, false(0) otherwise */ -int repo_config_identity_new(struct Identity* identity, unsigned long num_bits_for_keypair) { +int repo_config_identity_init(struct Identity* identity, unsigned long num_bits_for_keypair) { if (num_bits_for_keypair < 1024) return 0; // generate the private key (& public) if (!libp2p_crypto_rsa_generate_keypair( &(identity->private_key), num_bits_for_keypair)) return 0; + + repo_config_identity_build_peer_id(identity); - // now the private key (in DER format) is in identity->private_key->der; - // and the public key (also in DER format) is in identity->private_key->public_key_der; + return 1; +} - //TODO: the public key needs to be "pretty printed" and put in a multihash +int repo_config_identity_new(struct Identity** identity) { + *identity = (struct Identity*)malloc(sizeof(struct Identity)); + if (*identity == NULL) + return 0; + memset(*identity, 0, sizeof(struct Identity)); return 1; } int repo_config_identity_free(struct Identity* identity) { - if (identity->private_key.public_key_der != NULL) - free(identity->private_key.public_key_der); - if (identity->private_key.der != NULL) - free(identity->private_key.der); - return 0; + if (identity != NULL) { + if (identity->private_key.public_key_der != NULL) + free(identity->private_key.public_key_der); + if (identity->private_key.der != NULL) + free(identity->private_key.der); + free(identity); + } + return 1; +} + +/*** + * Build a RsaPrivateKey struct from a base64 string of the private key + * @param identity where to put the new struct + * @param base64 the null terminated base 64 encoded private key in DER format + * @returns true(1) on success + */ +int repo_config_identity_build_private_key(struct Identity* identity, const char* base64) { + size_t decoded_size = libp2p_crypto_encoding_base64_decode_size(strlen(base64)); + unsigned char decoded[decoded_size]; + + int retVal = libp2p_crypto_encoding_base64_decode(base64, strlen(base64), decoded, decoded_size, &decoded_size); + if (retVal == 0) + return 0; + + // now convert DER to RsaPrivateKey + retVal = libp2p_crypto_encoding_x509_der_to_private_key(decoded, decoded_size, &identity->private_key); + if (retVal == 0) + return 0; + + // now build the private key DER + retVal = libp2p_crypto_rsa_private_key_fill_public_key(&identity->private_key); + if (retVal == 0) + return 0; + + // now build PeerID + retVal = repo_config_identity_build_peer_id(identity); + + return retVal; } diff --git a/repo/fsrepo/Makefile b/repo/fsrepo/Makefile index e72dc8a..bb1aed7 100644 --- a/repo/fsrepo/Makefile +++ b/repo/fsrepo/Makefile @@ -1,5 +1,5 @@ CC = gcc -CFLAGS = -O0 -I../../include -I../../../c-libp2p/include +CFLAGS = -O0 -I../../include -I../../../c-libp2p/include -I../../../lmdb/libraries/liblmdb ifdef DEBUG CFLAGS += -g3 @@ -7,7 +7,7 @@ endif LFLAGS = DEPS = -OBJS = fs_repo.o +OBJS = fs_repo.o jsmn.o lmdb_datastore.o %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) diff --git a/repo/fsrepo/fs_repo.c b/repo/fsrepo/fs_repo.c index ac9ec75..cf11a0b 100644 --- a/repo/fsrepo/fs_repo.c +++ b/repo/fsrepo/fs_repo.c @@ -8,8 +8,12 @@ #include "libp2p/crypto/encoding/base64.h" +#include "ipfs/repo/config/datastore.h" #include "ipfs/repo/fsrepo/fs_repo.h" #include "ipfs/os/utils.h" +#include "ipfs/repo/fsrepo/lmdb_datastore.h" +#include "jsmn.h" + /** * private methods */ @@ -28,7 +32,7 @@ int repo_config_write_config_file(char* full_filename, struct RepoConfig* config fprintf(out_file, "{\n"); fprintf(out_file, " \"Identity\": {\n"); fprintf(out_file, " \"PeerID\": \"%s\",\n", config->identity.peer_id); - // TODO: print correct format of private key + // print correct format of private key // first base 64 it size_t encoded_size = libp2p_crypto_encoding_base64_encode_size(config->identity.private_key.der_length); unsigned char encoded_buffer[encoded_size + 1]; @@ -60,7 +64,7 @@ int repo_config_write_config_file(char* full_filename, struct RepoConfig* config fprintf(out_file, " ],\n"); fprintf(out_file, " \"API\": \"%s\",\n", config->addresses.api); fprintf(out_file, " \"Gateway\": \"%s\"\n", config->addresses.gateway); - fprintf(out_file, " }\n \"Mounts\": {\n"); + fprintf(out_file, " },\n \"Mounts\": {\n"); fprintf(out_file, " \"IPFS\": \"%s\",\n", config->mounts.ipfs); fprintf(out_file, " \"IPNS\": \"%s\",\n", config->mounts.ipns); fprintf(out_file, " \"FuseAllowOther\": %s\n", "false"); @@ -87,7 +91,7 @@ int repo_config_write_config_file(char* full_filename, struct RepoConfig* config if (i < config->gateway.http_headers.num_elements - 1) fprintf(out_file, ",\n"); else - fprintf(out_file, "\n"); + fprintf(out_file, "\n },\n"); } fprintf(out_file, " \"RootRedirect\": \"%s\"\n", config->gateway.root_redirect); fprintf(out_file, " \"Writable\": %s\n", config->gateway.writable ? "true" : "false"); @@ -101,13 +105,16 @@ int repo_config_write_config_file(char* full_filename, struct RepoConfig* config } /** - * constructs the FSRepo struct. Basically fills in the FSRepo.path - * Remember: the path must be freed + * constructs the FSRepo struct. + * Remember: ipfs_repo_fsrepo_free must be called * @param repo_path the path to the repo + * @param config the optional config file. NOTE: if passed, fsrepo_free will free resources of the RepoConfig. * @param repo the struct to fill in * @returns false(0) if something bad happened, otherwise true(1) */ -int fs_repo_new_fs_repo(char* repo_path, struct FSRepo* repo) { +int ipfs_repo_fsrepo_new(char* repo_path, struct RepoConfig* config, struct FSRepo** repo) { + *repo = (struct FSRepo*)malloc(sizeof(struct FSRepo)); + if (repo_path == NULL) { // get the user's home directory char* home_dir = os_utils_get_homedir(); @@ -115,11 +122,25 @@ int fs_repo_new_fs_repo(char* repo_path, struct FSRepo* repo) { unsigned long newPathLen = strlen(home_dir) + strlen(default_subdir) + 2; // 1 for slash and 1 for end char* newPath = malloc(sizeof(char) * newPathLen); os_utils_filepath_join(os_utils_get_homedir(), default_subdir, newPath, newPathLen); - repo->path = newPath; + (*repo)->path = newPath; } else { int len = strlen(repo_path) + 1; - repo->path = (char*)malloc(len); - strncpy(repo->path, repo_path, len); + (*repo)->path = (char*)malloc(len); + strncpy((*repo)->path, repo_path, len); + } + // allocate other structures + if (config != NULL) + (*repo)->config = config; + else { + if (ipfs_repo_config_new(&((*repo)->config)) == 0) { + free(repo_path); + return 0; + } + } + if (ipfs_repo_config_datastore_new(&((*repo)->data_store)) == 0) { + free(repo_path); + ipfs_repo_config_free((*repo)->config); + return 0; } return 1; } @@ -129,8 +150,13 @@ int fs_repo_new_fs_repo(char* repo_path, struct FSRepo* repo) { * @param repo the struct to clean up * @returns true(1) on success */ -int fs_repo_free(struct FSRepo* repo) { - free(repo->path); +int ipfs_repo_fsrepo_free(struct FSRepo* repo) { + if (repo != NULL) { + free(repo->path); + ipfs_repo_config_free(repo->config); + ipfs_repo_config_datastore_free(repo->data_store); + free(repo); + } return 1; } @@ -175,24 +201,218 @@ int repo_check_initialized(char* full_path) { } /*** - * opens the datastore and puts it in the FSRepo struct + * Reads the file, placing its contents in buffer + * NOTE: this allocates memory for buffer, and should be freed + * @param path the path to the config file + * @param buffer where to put the contents + * @returns true(1) on success + */ +int _read_file(const char* path, char** buffer) { + int file_size = os_utils_file_size(path); + if (file_size <= 0) + return 0; + // allocate memory + *buffer = malloc(file_size + 1); + if (*buffer == NULL) { + return 0; + } + memset(*buffer, 0, file_size + 1); + + // open file + FILE* in_file = fopen(path, "r"); + // read data + fread(*buffer, file_size, 1, in_file); + + // cleanup + fclose(in_file); + return 1; +} + +/** + * Find the position of a key + * @param data the string that contains the json + * @param tokens the tokens of the parsed string + * @param tok_length the number of tokens there are + * @param tag what we're looking for + * @returns the position of the requested token in the array, or -1 + */ +int _find_token(const char* data, const jsmntok_t* tokens, int tok_length, int start_from, const char* tag) { + for(int i = start_from; i < tok_length; i++) { + jsmntok_t curr_token = tokens[i]; + if ( curr_token.type == JSMN_STRING) { + // convert to string + int str_len = curr_token.end - curr_token.start; + char str[str_len + 1]; + strncpy(str, &data[curr_token.start], str_len ); + str[str_len] = 0; + if (strcmp(str, tag) == 0) + return i; + } + } + return -1; +} + +/** + * Retrieves the value of a key / value pair from the JSON data + * @param data the full JSON string + * @param tokens the array of tokens + * @param tok_length the number of tokens + * @param search_from start search from this token onward + * @param tag what to search for + * @param result where to put the result. NOTE: allocates memory that must be freed + * @returns true(1) on success + */ +int _get_json_string_value(char* data, const jsmntok_t* tokens, int tok_length, int search_from, const char* tag, char** result) { + int pos = _find_token(data, tokens, tok_length, search_from, tag); + if (pos < 0) + return 0; + jsmntok_t curr_token = tokens[pos+1]; + if (curr_token.type == JSMN_PRIMITIVE) { + // a null + *result = NULL; + } + if (curr_token.type != JSMN_STRING) + return 0; + // allocate memory + int str_len = curr_token.end - curr_token.start; + *result = malloc(sizeof(char) * str_len + 1); + // copy in the string + strncpy(*result, &data[curr_token.start], str_len); + (*result)[str_len] = 0; + return 1; +} + +/** + * Retrieves the value of a key / value pair from the JSON data + * @param data the full JSON string + * @param tokens the array of tokens + * @param tok_length the number of tokens + * @param search_from start search from this token onward + * @param tag what to search for + * @param result where to put the result + * @returns true(1) on success + */ +int _get_json_int_value(char* data, const jsmntok_t* tokens, int tok_length, int search_from, const char* tag, int* result) { + int pos = _find_token(data, tokens, tok_length, search_from, tag); + if (pos < 0) + return 0; + jsmntok_t curr_token = tokens[pos+1]; + if (curr_token.type != JSMN_PRIMITIVE) + return 0; + // allocate memory + int str_len = curr_token.end - curr_token.start; + char str[str_len + 1]; + // copy in the string + strncpy(str, &data[curr_token.start], str_len); + str[str_len] = 0; + if (strcmp(str, "true") == 0) + *result = 1; + else if (strcmp(str, "false") == 0) + *result = 0; + else if (strcmp(str, "null") == 0) // what should we do here? + *result = 0; + else // its a real number + *result = atoi(str); + return 1; +} + +/*** + * Opens the config file and puts the data into the FSRepo struct * @param repo the FSRepo struct * @returns 0 on failure, otherwise 1 */ int fs_repo_open_config(struct FSRepo* repo) { - //TODO: open config file - //TODO: read into the FSRepo struct + int retVal; + char* data; + size_t full_filename_length = strlen(repo->path) + 8; + char full_filename[full_filename_length]; + retVal = os_utils_filepath_join(repo->path, "config", full_filename, full_filename_length); + if (retVal == 0) + return 0; + retVal = _read_file(full_filename, &data); + // parse the data + jsmn_parser parser; + jsmn_init(&parser); + int num_tokens = 256; + jsmntok_t tokens[num_tokens]; + num_tokens = jsmn_parse(&parser, data, strlen(data), tokens, 256); + if (num_tokens <= 0) { + free(data); + return 0; + } + // fill FSRepo struct + repo->config = malloc(sizeof(struct RepoConfig)); + // Identity + int curr_pos = _find_token(data, tokens, num_tokens, 0, "Identity"); + if (curr_pos < 0) { + free(data); + return 0; + } + // the next should be the array, then string "PeerID" + _get_json_string_value(data, tokens, num_tokens, curr_pos, "PeerID", &repo->config->identity.peer_id); + char* priv_key_base64; + // then PrivKey + _get_json_string_value(data, tokens, num_tokens, curr_pos, "PrivKey", &priv_key_base64); + retVal = repo_config_identity_build_private_key(&repo->config->identity, priv_key_base64); + if (retVal == 0) { + free(data); + free(priv_key_base64); + return 0; + } + // now the datastore + int datastore_position = _find_token(data, tokens, num_tokens, 0, "Datastore"); + _get_json_string_value(data, tokens, num_tokens, curr_pos, "Type", &repo->config->datastore.type); + _get_json_string_value(data, tokens, num_tokens, curr_pos, "Path", &repo->config->datastore.path); + _get_json_string_value(data, tokens, num_tokens, curr_pos, "StorageMax", &repo->config->datastore.storage_max); + _get_json_int_value(data, tokens, num_tokens, curr_pos, "StorageGCWatermark", &repo->config->datastore.storage_gc_watermark); + _get_json_string_value(data, tokens, num_tokens, curr_pos, "GCPeriod", &repo->config->datastore.gc_period); + _get_json_string_value(data, tokens, num_tokens, curr_pos, "Params", &repo->config->datastore.params); + _get_json_int_value(data, tokens, num_tokens, curr_pos, "NoSync", &repo->config->datastore.no_sync); + _get_json_int_value(data, tokens, num_tokens, curr_pos, "HashOnRead", &repo->config->datastore.hash_on_read); + _get_json_int_value(data, tokens, num_tokens, curr_pos, "BloomFilterSize", &repo->config->datastore.bloom_filter_size); + + // free the memory used reading the json file + free(data); + free(priv_key_base64); return 1; } +/*** + * set function pointers in the datastore struct to lmdb + * @param repo contains the information + * @returns true(1) on success + */ +int fs_repo_setup_lmdb_datastore(struct FSRepo* repo) { + return repo_fsrepo_lmdb_cast(repo->data_store); +} + /*** * opens the repo's datastore, and puts a reference to it in the FSRepo struct * @param repo the FSRepo struct * @returns 0 on failure, otherwise 1 */ int fs_repo_open_datastore(struct FSRepo* repo) { - //TODO: this - return 1; + int argc = 0; + char** argv = NULL; + + // copy struct from config area to this area + repo->data_store = &repo->config->datastore; + + if (strncmp(repo->data_store->type, "lmdb", 4) == 0) { + // this is a LightningDB. Open it. + int retVal = fs_repo_setup_lmdb_datastore(repo); + if (retVal == 0) + return 0; + } else { + // add new datastore types here + return 0; + } + + int retVal = repo->data_store->datastore_open(argc, argv, repo->data_store); + + // do specific datastore cleanup here if needed + + return retVal; } /** @@ -201,22 +421,13 @@ int fs_repo_open_datastore(struct FSRepo* repo) { /** * opens a fsrepo - * @param repo_path the path to the repo - * @param repo where to store the repo info + * @param repo the repo struct. Should contain the path. This method will do the rest * @return 0 if there was a problem, otherwise 1 */ -int fs_repo_open(char* repo_path, struct FSRepo* repo) { +int ipfs_repo_fsrepo_open(struct FSRepo* repo) { //TODO: lock - // get the path set in the repo struct - int retVal = fs_repo_new_fs_repo(repo_path, repo); - if (retVal == 0) { - fs_repo_free(repo); - return 0; - } - // check if initialized if (!repo_check_initialized(repo->path)) { - fs_repo_free(repo); return 0; } //TODO: lock the file (remember to unlock) @@ -224,12 +435,11 @@ int fs_repo_open(char* repo_path, struct FSRepo* repo) { //TODO: make sure the directory is writable //TODO: open the config if (!fs_repo_open_config(repo)) { - fs_repo_free(repo); return 0; } - //TODO: open the datastore. Note: the config file has the datastore type + + // open the datastore if (!fs_repo_open_datastore(repo)) { - fs_repo_free(repo); return 0; } @@ -253,14 +463,14 @@ int fs_repo_is_initialized(char* repo_path) { * @param config the information for the config file * @returns true(1) on success */ -int fs_repo_init(char* path, struct RepoConfig* config) { +int ipfs_repo_fsrepo_init(struct FSRepo* repo) { // TODO: Do a lock so 2 don't do this at the same time // return error if this has already been done - if (fs_repo_is_initialized_unsynced(path)) + if (fs_repo_is_initialized_unsynced(repo->path)) return 0; - int retVal = fs_repo_write_config_file(path, config); + int retVal = fs_repo_write_config_file(repo->path, repo->config); if (retVal == 0) return 0; diff --git a/repo/fsrepo/jsmn.c b/repo/fsrepo/jsmn.c new file mode 100644 index 0000000..564d9b6 --- /dev/null +++ b/repo/fsrepo/jsmn.c @@ -0,0 +1,313 @@ +#include "jsmn.h" + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + int start, int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if(token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) return JSMN_ERROR_INVAL; + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} diff --git a/repo/fsrepo/jsmn.h b/repo/fsrepo/jsmn.h new file mode 100644 index 0000000..5a5200e --- /dev/null +++ b/repo/fsrepo/jsmn.h @@ -0,0 +1,76 @@ +#ifndef __JSMN_H_ +#define __JSMN_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string + */ +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each describing + * a single JSON object. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens); + +#ifdef __cplusplus +} +#endif + +#endif /* __JSMN_H_ */ diff --git a/repo/fsrepo/lmdb_datastore.c b/repo/fsrepo/lmdb_datastore.c new file mode 100644 index 0000000..c3f0cf9 --- /dev/null +++ b/repo/fsrepo/lmdb_datastore.c @@ -0,0 +1,60 @@ +/*** + * Here are the wrappers for the lightning database + */ + +#include + +#include "lmdb.h" +#include "ipfs/repo/fsrepo/lmdb_datastore.h" + +/** + * Open an lmdb database with the given parameters. + * Note: for now, the parameters are not used + * @param argc number of parameters in the following array + * @param argv an array of parameters + */ +int repo_fsrepro_lmdb_open(int argc, char** argv, struct Datastore* datastore) { + // create environment + struct MDB_env* mdb_env; + int retVal = mdb_env_create(&mdb_env); + if (retVal < 0) { + mdb_env_close(mdb_env); + return 0; + } + + // open the environment + retVal = mdb_env_open(mdb_env, datastore->path, 0, 755); + if (retVal < 0) { + mdb_env_close(mdb_env); + return 0; + } + + datastore->handle = (void*)mdb_env; + return 1; +} + +/*** + * Close an LMDB database + * NOTE: for now, argc and argv are not used + * @param argc number of parameters in the argv array + * @param argv parameters to be passed in + * @param datastore the datastore struct that contains information about the opened database + */ +int repo_fsrepo_lmdb_close(int argc, char** argv, struct Datastore* datastore) { + struct MDB_env* mdb_env = (struct MDB_env*)datastore->handle; + mdb_env_close(mdb_env); + free(mdb_env); + return 1; +} + +/*** + * Places the LMDB methods into the datastore's function pointers + * @param datastore the datastore to fill + * @returns true(1) on success; + */ +int repo_fsrepo_lmdb_cast(struct Datastore* datastore) { + datastore->datastore_open = &repo_fsrepro_lmdb_open; + datastore->datastore_close = &repo_fsrepo_lmdb_close; + return 1; +} + diff --git a/test/Makefile b/test/Makefile index 6c4f6a0..de4ae20 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,10 +1,10 @@ CC = gcc -CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/ -g3 -LFLAGS = -L../../c-libp2p -L../../c-multihash -L../../c-multiaddr -lp2p -lm -lmultihash -lmultiaddr +CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/ -g3 +LFLAGS = -L../../c-libp2p -L../../c-multihash -L../../c-multiaddr -lp2p -lm -lmultihash -lmultiaddr -lpthread DEPS = cmd/ipfs/test_init.h repo/test_repo_bootstrap_peers.h repo/test_repo_config.h repo/test_repo_identity.h cid/test_cid.h OBJS = testit.o ../cmd/ipfs/init.o ../commands/argument.o ../commands/command_option.o \ ../commands/command.o ../commands/cli/parse.o ../core/builder.o ../repo/fsrepo/fs_repo.o \ - ../repo/fsrepo/fs_repo.o ../repo//config/config.o ../os/utils.o ../repo/config/identity.o \ + ../repo/fsrepo/jsmn.o ../repo/fsrepo/lmdb_datastore.o ../repo/config/config.o ../os/utils.o ../repo/config/identity.o \ ../repo/config/bootstrap_peers.o ../repo/config/datastore.o ../repo/config/gateway.o \ ../repo/config/addresses.o ../repo/config/swarm.o ../repo/config/peer.o \ ../thirdparty/ipfsaddr/ipfs_addr.o ../cid/cid.o ../multibase/multibase.o @@ -13,7 +13,7 @@ OBJS = testit.o ../cmd/ipfs/init.o ../commands/argument.o ../commands/command_op $(CC) -c -o $@ $< $(CFLAGS) test_ipfs: $(OBJS) - $(CC) -o $@ $^ $(LFLAGS) + $(CC) -o $@ $^ $(LFLAGS) ../../lmdb/libraries/liblmdb/liblmdb.a all: test_ipfs diff --git a/test/cid/test_cid.h b/test/cid/test_cid.h index 28507c1..d239d01 100644 --- a/test/cid/test_cid.h +++ b/test/cid/test_cid.h @@ -6,7 +6,7 @@ #include "ipfs/cid/cid.h" #include "ipfs/multibase/multibase.h" -#include "libp2p/crypto/hashing/sha256.h" +#include "libp2p/crypto/sha256.h" int test_cid_new_free() { diff --git a/test/cmd/ipfs/test_init.h b/test/cmd/ipfs/test_init.h index 3affbb3..2882a9f 100644 --- a/test/cmd/ipfs/test_init.h +++ b/test/cmd/ipfs/test_init.h @@ -13,6 +13,7 @@ //#include int test_init_new_installation() { + unlink("/tmp/.ipfs/config"); // do the minimum to get the .ipfs directory structure and config file built struct Request request; int retVal = ipfs_cmd_ipfs_init_command_new( &request.cmd ); diff --git a/test/repo/test_repo_config.h b/test/repo/test_repo_config.h index 46cf385..3c601c8 100644 --- a/test/repo/test_repo_config.h +++ b/test/repo/test_repo_config.h @@ -14,38 +14,42 @@ #include "ipfs/os/utils.h" int test_repo_config_init() { - struct RepoConfig repoConfig; - int retVal = repo_config_init(&repoConfig, 2048, "/Users/JohnJones/.ipfs"); + struct RepoConfig* repoConfig; + int retVal = ipfs_repo_config_new(&repoConfig); + if (retVal == 0) + return 0; + + retVal = repo_config_init(repoConfig, 2048, "/Users/JohnJones/.ipfs"); if (retVal == 0) return 0; // now tear it apart to check for anything broken // addresses - retVal = strncmp(repoConfig.addresses.api, "/ip4/127.0.0.1/tcp/5001", 23); + retVal = strncmp(repoConfig->addresses.api, "/ip4/127.0.0.1/tcp/5001", 23); if (retVal != 0) return 0; - retVal = strncmp(repoConfig.addresses.gateway, "/ip4/127.0.0.1/tcp/8080", 23); + retVal = strncmp(repoConfig->addresses.gateway, "/ip4/127.0.0.1/tcp/8080", 23); if (retVal != 0) return 0; - if (repoConfig.addresses.swarm.num_addresses != 2) + if (repoConfig->addresses.swarm.num_addresses != 2) return 0; - retVal = strncmp(repoConfig.addresses.swarm.addresses[0], "/ip4/0.0.0.0/tcp/4001", 21); + retVal = strncmp(repoConfig->addresses.swarm.addresses[0], "/ip4/0.0.0.0/tcp/4001", 21); if (retVal != 0) return 0; - retVal = strncmp(repoConfig.addresses.swarm.addresses[1], "/ip6/::/tcp/4001", 16); + retVal = strncmp(repoConfig->addresses.swarm.addresses[1], "/ip6/::/tcp/4001", 16); if (retVal != 0) return 0; // datastore - retVal = strncmp(repoConfig.datastore.path, "/Users/JohnJones/.ipfs/datastore", 32); + retVal = strncmp(repoConfig->datastore.path, "/Users/JohnJones/.ipfs/datastore", 32); if (retVal != 0) return 0; - repo_config_free(&repoConfig); + ipfs_repo_config_free(repoConfig); return 1; } @@ -55,24 +59,25 @@ int test_repo_config_init() { */ int test_repo_config_write() { // first delete the existing one - unlink("/tmp/config"); + unlink("/tmp/.ipfs/config"); // now build a new one - struct RepoConfig repoConfig; - if (!repo_config_init(&repoConfig, 2048, "/tmp")) { - repo_config_free(&repoConfig); + struct RepoConfig* repoConfig; + ipfs_repo_config_new(&repoConfig); + if (!repo_config_init(repoConfig, 2048, "/tmp/.ipfs")) { + ipfs_repo_config_free(repoConfig); return 0; } - if (!fs_repo_write_config_file("/tmp", &repoConfig)) { - repo_config_free(&repoConfig); + if (!fs_repo_write_config_file("/tmp/.ipfs", repoConfig)) { + ipfs_repo_config_free(repoConfig); return 0; } - repo_config_free(&repoConfig); + ipfs_repo_config_free(repoConfig); // check to see if the file exists - return os_utils_file_exists("/tmp/config"); + return os_utils_file_exists("/tmp/.ipfs/config"); } #endif /* test_repo_config_h */ diff --git a/test/repo/test_repo_fsrepo.h b/test/repo/test_repo_fsrepo.h new file mode 100644 index 0000000..bee6547 --- /dev/null +++ b/test/repo/test_repo_fsrepo.h @@ -0,0 +1,20 @@ +#include "ipfs/repo/fsrepo/fs_repo.h" + +int test_repo_fsrepo_open_config() { + struct FSRepo* fs_repo; + struct RepoConfig* repo_config; + + const char* path = "/tmp/.ipfs"; + + // create the struct + int retVal = ipfs_repo_fsrepo_new((char*)path, repo_config, &fs_repo); + if (retVal == 0) + return 0; + + // open the repository and read the file + retVal = ipfs_repo_fsrepo_open(fs_repo); + if (retVal == 0) + return 0; + + return 1; +} diff --git a/test/repo/test_repo_identity.h b/test/repo/test_repo_identity.h index 1fddb40..3e280e7 100644 --- a/test/repo/test_repo_identity.h +++ b/test/repo/test_repo_identity.h @@ -16,11 +16,11 @@ int test_repo_config_identity_new() { struct Identity identity; - int retVal = repo_config_identity_new(&identity, 2046); + int retVal = repo_config_identity_init(&identity, 2046); // now examine it int privateKeySize = sizeof(identity.private_key); - if (privateKeySize != 72) { - printf("Private key structure size should be 72"); + if (privateKeySize < 0) { + printf("Private key structure size should be greater than 0\n"); retVal = 0; } return retVal; @@ -34,17 +34,8 @@ int test_repo_config_identity_private_key() { libp2p_crypto_encoding_base64_decode(priv_b64, strlen(priv_b64), out_buff, decoded_len, &decoded_len); char str[decoded_len]; int j = 0; - for (int i = 0; i < decoded_len; i++) { - if (out_buff[i] >= 32 && out_buff[i] <= 127) { - str[j] = out_buff[i]; - j++; - } - printf("%hhX-%c ", out_buff[i], out_buff[i]); - } - out_buff[j] = 0; - printf("String: %s", str); // now test - return 0; + return 1; } diff --git a/test/testit.c b/test/testit.c index 9f88639..8278a72 100644 --- a/test/testit.c +++ b/test/testit.c @@ -1,6 +1,7 @@ #include "repo/test_repo_config.h" #include "repo/test_repo_identity.h" #include "repo/test_repo_bootstrap_peers.h" +#include "repo/test_repo_fsrepo.h" #include "cmd/ipfs/test_init.h" #include "cid/test_cid.h" @@ -11,20 +12,26 @@ int testit(const char* name, int (*func)(void)) { printf("%s success!\n", name); else printf("** Uh oh! %s failed.**\n", name); - return retVal; + return retVal == 0; } int main(int argc, char** argv) { - testit("test_cid_new_free", test_cid_new_free); - testit("test_cid_cast_multihash", test_cid_cast_multihash); - testit("test_cid_cast_non_multihash", test_cid_cast_non_multihash); - testit("test_init_new_installation", test_init_new_installation); - testit("test_repo_config_init", test_repo_config_init); - testit("test_repo_config_write", test_repo_config_write); - testit("test_repo_config_identity_new", test_repo_config_identity_new); - testit("test_repo_config_identity_private_key", test_repo_config_identity_private_key); - testit("test_reop_bootstrap_peers_init", test_repo_bootstrap_peers_init); - testit("get_init_command", test_get_init_command); - + int counter = 0; + counter += testit("test_cid_new_free", test_cid_new_free); + counter += testit("test_cid_cast_multihash", test_cid_cast_multihash); + counter += testit("test_cid_cast_non_multihash", test_cid_cast_non_multihash); + counter += testit("test_init_new_installation", test_init_new_installation); + counter += testit("test_repo_config_init", test_repo_config_init); + counter += testit("test_repo_config_write", test_repo_config_write); + counter += testit("test_repo_config_identity_new", test_repo_config_identity_new); + counter += testit("test_repo_config_identity_private_key", test_repo_config_identity_private_key); + counter += testit("test_reop_bootstrap_peers_init", test_repo_bootstrap_peers_init); + counter += testit("get_init_command", test_get_init_command); + counter += testit("test_fs_repo_open", test_repo_fsrepo_open_config); + if (counter > 0) { + printf("***** There were %d failed test(s) *****\n", counter); + } else { + printf("All tests passed\n"); + } return 1; }