c-ipfs/blocks/blockstore.c

366 lines
10 KiB
C

/***
* a thin wrapper over a datastore for getting and putting block objects
*/
#include "libp2p/crypto/encoding/base32.h"
#include "ipfs/cid/cid.h"
#include "ipfs/blocks/block.h"
#include "ipfs/blocks/blockstore.h"
#include "ipfs/datastore/ds_helper.h"
#include "ipfs/repo/fsrepo/fs_repo.h"
#include "libp2p/os/utils.h"
/***
* Create a new Blockstore struct
* @param fs_repo the FSRepo to use
* @returns the new Blockstore struct, or NULL if there was a problem.
*/
struct Blockstore* ipfs_blockstore_new(const struct FSRepo* fs_repo) {
struct Blockstore* blockstore = (struct Blockstore*) malloc(sizeof(struct Blockstore));
if(blockstore != NULL) {
blockstore->blockstoreContext = (struct BlockstoreContext*) malloc(sizeof(struct BlockstoreContext));
if (blockstore->blockstoreContext == NULL) {
free(blockstore);
return NULL;
}
blockstore->blockstoreContext->fs_repo = fs_repo;
blockstore->Delete = ipfs_blockstore_delete;
blockstore->Get = ipfs_blockstore_get;
blockstore->Has = ipfs_blockstore_has;
blockstore->Put = ipfs_blockstore_put;
}
return blockstore;
}
/**
* Release resources of a Blockstore struct
* @param blockstore the struct to free
* @returns true(1)
*/
int ipfs_blockstore_free(struct Blockstore* blockstore) {
if (blockstore != NULL) {
if (blockstore->blockstoreContext != NULL)
free(blockstore->blockstoreContext);
free(blockstore);
}
return 1;
}
/**
* Delete a block based on its Cid
* @param cid the Cid to look for
* @param returns true(1) on success
*/
int ipfs_blockstore_delete(const struct BlockstoreContext* context, 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(const struct BlockstoreContext* context, struct Cid* cid) {
return 0;
}
unsigned char* ipfs_blockstore_cid_to_base32(const struct Cid* cid) {
size_t key_length = libp2p_crypto_encoding_base32_encode_size(cid->hash_length);
unsigned char* buffer = (unsigned char*)malloc(key_length + 1);
if (buffer == NULL)
return NULL;
int retVal = ipfs_datastore_helper_ds_key_from_binary(cid->hash, cid->hash_length, &buffer[0], key_length, &key_length);
if (retVal == 0) {
free(buffer);
return NULL;
}
buffer[key_length] = 0;
return buffer;
}
unsigned char* ipfs_blockstore_hash_to_base32(const unsigned char* hash, size_t hash_length) {
size_t key_length = libp2p_crypto_encoding_base32_encode_size(hash_length);
unsigned char* buffer = (unsigned char*)malloc(key_length + 1);
if (buffer == NULL)
return NULL;
int retVal = ipfs_datastore_helper_ds_key_from_binary(hash, hash_length, &buffer[0], key_length, &key_length);
if (retVal == 0) {
free(buffer);
return NULL;
}
buffer[key_length] = 0;
return buffer;
}
char* ipfs_blockstore_path_get(const struct FSRepo* fs_repo, const char* filename) {
int filepath_size = strlen(fs_repo->path) + 12;
char filepath[filepath_size];
int retVal = os_utils_filepath_join(fs_repo->path, "blockstore", filepath, filepath_size);
if (retVal == 0) {
free(filepath);
return 0;
}
int complete_filename_size = strlen(filepath) + strlen(filename) + 2;
char* complete_filename = (char*)malloc(complete_filename_size);
if (complete_filename == NULL)
return NULL;
retVal = os_utils_filepath_join(filepath, filename, complete_filename, complete_filename_size);
return complete_filename;
}
/***
* 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(const struct BlockstoreContext* context, struct Cid* cid, struct Block** block) {
int retVal = 0;
// get datastore key, which is a base32 key of the multihash
unsigned char* key = ipfs_blockstore_hash_to_base32(cid->hash, cid->hash_length);
char* filename = ipfs_blockstore_path_get(context->fs_repo, (char*)key);
size_t file_size = os_utils_file_size(filename);
unsigned char buffer[file_size];
FILE* file = fopen(filename, "rb");
if (file == NULL)
goto exit;
size_t bytes_read = fread(buffer, 1, file_size, file);
fclose(file);
if (!ipfs_blocks_block_protobuf_decode(buffer, bytes_read, block))
goto exit;
(*block)->cid = ipfs_cid_copy(cid);
retVal = 1;
exit:
free(key);
free(filename);
return retVal;
}
/***
* Put a block in the blockstore
* @param block the block to store
* @returns true(1) on success
*/
int ipfs_blockstore_put(const struct BlockstoreContext* context, struct Block* block, size_t* bytes_written) {
// from blockstore.go line 118
int retVal = 0;
// Get Datastore key, which is a base32 key of the multihash,
unsigned char* key = ipfs_blockstore_cid_to_base32(block->cid);
if (key == NULL) {
free(key);
return 0;
}
//TODO: put this in subdirectories
// turn the block into a binary array
size_t protobuf_len = ipfs_blocks_block_protobuf_encode_size(block);
unsigned char protobuf[protobuf_len];
retVal = ipfs_blocks_block_protobuf_encode(block, protobuf, protobuf_len, &protobuf_len);
if (retVal == 0) {
free(key);
return 0;
}
// now write byte array to file
char* filename = ipfs_blockstore_path_get(context->fs_repo, (char*)key);
if (filename == NULL) {
free(key);
return 0;
}
FILE* file = fopen(filename, "wb");
*bytes_written = fwrite(protobuf, 1, protobuf_len, file);
fclose(file);
if (*bytes_written != protobuf_len) {
free(key);
free(filename);
return 0;
}
// send to Put with key (this is now done separately)
//fs_repo->config->datastore->datastore_put(key, key_length, block->data, block->data_length, fs_repo->config->datastore);
free(key);
free(filename);
return 1;
}
/***
* Put a struct UnixFS in the blockstore
* @param unix_fs the structure
* @param fs_repo the repo to place the strucure in
* @param bytes_written the number of bytes written to the blockstore
* @returns true(1) on success
*/
int ipfs_blockstore_put_unixfs(const struct UnixFS* unix_fs, const struct FSRepo* fs_repo, size_t* bytes_written) {
// from blockstore.go line 118
int retVal = 0;
// Get Datastore key, which is a base32 key of the multihash,
unsigned char* key = ipfs_blockstore_hash_to_base32(unix_fs->hash, unix_fs->hash_length);
if (key == NULL) {
free(key);
return 0;
}
//TODO: put this in subdirectories
// turn the block into a binary array
size_t protobuf_len = ipfs_unixfs_protobuf_encode_size(unix_fs);
unsigned char protobuf[protobuf_len];
retVal = ipfs_unixfs_protobuf_encode(unix_fs, protobuf, protobuf_len, &protobuf_len);
if (retVal == 0) {
free(key);
return 0;
}
// now write byte array to file
char* filename = ipfs_blockstore_path_get(fs_repo, (char*)key);
if (filename == NULL) {
free(key);
return 0;
}
FILE* file = fopen(filename, "wb");
*bytes_written = fwrite(protobuf, 1, protobuf_len, file);
fclose(file);
if (*bytes_written != protobuf_len) {
free(key);
free(filename);
return 0;
}
free(key);
free(filename);
return 1;
}
/***
* Find a UnixFS struct based on its hash
* @param hash the hash to look for
* @param hash_length the length of the hash
* @param unix_fs the struct to fill
* @param fs_repo where to look for the data
* @returns true(1) on success
*/
int ipfs_blockstore_get_unixfs(const unsigned char* hash, size_t hash_length, struct UnixFS** block, const struct FSRepo* fs_repo) {
// get datastore key, which is a base32 key of the multihash
unsigned char* key = ipfs_blockstore_hash_to_base32(hash, hash_length);
char* filename = ipfs_blockstore_path_get(fs_repo, (char*)key);
size_t file_size = os_utils_file_size(filename);
unsigned char buffer[file_size];
FILE* file = fopen(filename, "rb");
size_t bytes_read = fread(buffer, 1, file_size, file);
fclose(file);
int retVal = ipfs_unixfs_protobuf_decode(buffer, bytes_read, block);
free(key);
free(filename);
return retVal;
}
/***
* Put a struct Node in the blockstore
* @param node the structure
* @param fs_repo the repo to place the strucure in
* @param bytes_written the number of bytes written to the blockstore
* @returns true(1) on success
*/
int ipfs_blockstore_put_node(const struct HashtableNode* node, const struct FSRepo* fs_repo, size_t* bytes_written) {
// from blockstore.go line 118
int retVal = 0;
// Get Datastore key, which is a base32 key of the multihash,
unsigned char* key = ipfs_blockstore_hash_to_base32(node->hash, node->hash_size);
if (key == NULL) {
free(key);
return 0;
}
//TODO: put this in subdirectories
// turn the block into a binary array
size_t protobuf_len = ipfs_hashtable_node_protobuf_encode_size(node);
unsigned char protobuf[protobuf_len];
retVal = ipfs_hashtable_node_protobuf_encode(node, protobuf, protobuf_len, &protobuf_len);
if (retVal == 0) {
free(key);
return 0;
}
// now write byte array to file
char* filename = ipfs_blockstore_path_get(fs_repo, (char*)key);
if (filename == NULL) {
free(key);
return 0;
}
FILE* file = fopen(filename, "wb");
*bytes_written = fwrite(protobuf, 1, protobuf_len, file);
fclose(file);
if (*bytes_written != protobuf_len) {
free(key);
free(filename);
return 0;
}
free(key);
free(filename);
return 1;
}
/***
* Find a UnixFS struct based on its hash
* @param hash the hash to look for
* @param hash_length the length of the hash
* @param unix_fs the struct to fill
* @param fs_repo where to look for the data
* @returns true(1) on success
*/
int ipfs_blockstore_get_node(const unsigned char* hash, size_t hash_length, struct HashtableNode** node, const struct FSRepo* fs_repo) {
// get datastore key, which is a base32 key of the multihash
unsigned char* key = ipfs_blockstore_hash_to_base32(hash, hash_length);
char* filename = ipfs_blockstore_path_get(fs_repo, (char*)key);
size_t file_size = os_utils_file_size(filename);
unsigned char buffer[file_size];
FILE* file = fopen(filename, "rb");
size_t bytes_read = fread(buffer, 1, file_size, file);
fclose(file);
// now we have the block, convert it to a node
struct Block* block;
if (!ipfs_blocks_block_protobuf_decode(buffer, bytes_read, &block)) {
free(key);
free(filename);
ipfs_block_free(block);
return 0;
}
int retVal = ipfs_hashtable_node_protobuf_decode(block->data, block->data_length, node);
free(key);
free(filename);
ipfs_block_free(block);
return retVal;
}