From c2fe60949e9325de003b9ce6de92f7c612bfafa3 Mon Sep 17 00:00:00 2001 From: jmjatlanta Date: Thu, 29 Dec 2016 19:05:44 -0500 Subject: [PATCH] Added the ability to retrieve the file using directories Files can be refered to directly using their hash, or the hash of their directory and the file name. --- importer/Makefile | 2 +- importer/resolver.c | 142 +++++++++++++++++++++++++++++ include/ipfs/importer/resolver.h | 12 +++ include/ipfs/merkledag/merkledag.h | 9 ++ include/ipfs/merkledag/node.h | 7 ++ main/Makefile | 3 +- merkledag/merkledag.c | 10 ++ merkledag/node.c | 13 +++ test/Makefile | 3 +- test/node/test_resolver.h | 34 +++++++ test/testit.c | 3 + 11 files changed, 233 insertions(+), 5 deletions(-) create mode 100644 importer/resolver.c create mode 100644 include/ipfs/importer/resolver.h create mode 100644 test/node/test_resolver.h diff --git a/importer/Makefile b/importer/Makefile index 158f9d6..7a28ea2 100644 --- a/importer/Makefile +++ b/importer/Makefile @@ -7,7 +7,7 @@ endif LFLAGS = DEPS = -OBJS = importer.o exporter.o +OBJS = importer.o exporter.o resolver.o %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) diff --git a/importer/resolver.c b/importer/resolver.c new file mode 100644 index 0000000..5850dc3 --- /dev/null +++ b/importer/resolver.c @@ -0,0 +1,142 @@ +#include +#include +#include + +#include "libp2p/crypto/encoding/base58.h" +#include "ipfs/merkledag/node.h" +#include "ipfs/merkledag/merkledag.h" +#include "ipfs/repo/fsrepo/fs_repo.h" + +/** + * return the next chunk of a path + * @param path the path + * @param next_part a pointer to a string NOTE: don't forget to free + * @returns true(1) on success, false(0) on error, or no more parts + */ +int ipfs_resolver_next_path(const char* path, char** next_part) { + for (int i = 0; i < strlen(path); i++) { + if (path[i] != '/') { // we have the next section + char* pos = strchr(&path[i+1], '/'); + if (pos == NULL) { + *next_part = (char*)malloc(strlen(path) + 1); + strcpy(*next_part, path); + } else { + *next_part = (char*)malloc(pos - &path[i] + 1); + strncpy(*next_part, &path[i], pos-&path[i]); + (*next_part)[pos-&path[i]] = 0; + } + return 1; + } + } + return 0; +} + +/** + * Remove preceding slash and "/ipfs/" or "/ipns/" + * @param path + */ +const char* ipfs_resolver_remove_path_prefix(const char* path) { + int pos = 0; + int first_non_slash = -1; + while(&path[pos] != NULL) { + if (path[pos] == '/') { + pos++; + continue; + } else { + if (first_non_slash == -1) + first_non_slash = pos; + if (pos == first_non_slash && (strncmp(&path[pos], "ipfs", 4) == 0 || strncmp(&path[pos], "ipns", 4) == 0) ) { + // ipfs or ipns should be up front. Otherwise, it could be part of the path + pos += 4; + } else { + return &path[pos]; + } + } + } + return NULL; +} + +/** + * Interogate the path and the current node, looking + * for the desired node. + * @param path the current path + * @param from the current node (or NULL if it is the first call) + * @returns what we are looking for, or NULL if it wasn't found + */ +struct Node* ipfs_resolver_get(const char* path, struct Node* from, const struct FSRepo* fs_repo) { + // remove unnecessary stuff + if (from == NULL) + path = ipfs_resolver_remove_path_prefix(path); + // grab the portion of the path to work with + char* path_section; + if (ipfs_resolver_next_path(path, &path_section) == 0) + return NULL; + struct Node* current_node = NULL; + if (from == NULL) { + // this is the first time around. Grab the root node + if (path_section[0] == 'Q' && path_section[1] == 'm') { + // we have a hash. Convert to a real hash, and find the node + size_t hash_length = libp2p_crypto_encoding_base58_decode_size(strlen(path_section)); + unsigned char hash[hash_length]; + unsigned char* ptr = &hash[0]; + if (libp2p_crypto_encoding_base58_decode((unsigned char*)path_section, strlen(path_section), &ptr, &hash_length) == 0) { + free(path_section); + return NULL; + } + if (ipfs_merkledag_get_by_multihash(hash, hash_length, ¤t_node, fs_repo) == 0) { + free(path_section); + return NULL; + } + // we have the root node, now see if we want this or something further down + int pos = strlen(path_section); + if (pos == strlen(path)) { + return current_node; + } else { + // look on... + return ipfs_resolver_get(&path[pos+1], current_node, fs_repo); // the +1 is the slash + } + } else { + // we don't have a current node, and we don't have a hash. Something is wrong + free(path_section); + return NULL; + } + } else { + // we were passed a node. If it is a directory, see if what we're looking for is in it + if (ipfs_node_is_directory(from)) { + struct NodeLink* curr_link = from->head_link; + while (curr_link != NULL) { + // if it matches the name, we found what we're looking for. + // If so, load up the node by its hash + if (strcmp(curr_link->name, path_section) == 0) { + if (ipfs_merkledag_get(curr_link->hash, curr_link->hash_size, ¤t_node, fs_repo) == 0) { + free(path_section); + return NULL; + } + if (strlen(path_section) == strlen(path)) { + // we are at the end of our search + ipfs_node_free(from); + return current_node; + } else { + char* next_path_section; + ipfs_resolver_next_path(&path[strlen(path_section)], &next_path_section); + free(path_section); + // if we're at the end of the path, return the node + // continue looking for the next part of the path + ipfs_node_free(from); + return ipfs_resolver_get(next_path_section, current_node, fs_repo); + } + } + curr_link = curr_link->next; + } + } else { + // we're asking for a file from an object that is not a directory. Bail. + free(path_section); + return NULL; + } + } + // it should never get here + free(path_section); + if (from != NULL) + ipfs_node_free(from); + return NULL; +} diff --git a/include/ipfs/importer/resolver.h b/include/ipfs/importer/resolver.h new file mode 100644 index 0000000..ef3af91 --- /dev/null +++ b/include/ipfs/importer/resolver.h @@ -0,0 +1,12 @@ +#pragma once + +#include "ipfs/merkledag/node.h" + +/** + * Interogate the path and the current node, looking + * for the desired node. + * @param path the current path + * @param from the current node (or NULL if it is the first call) + * @returns what we are looking for, or NULL if it wasn't found + */ +struct Node* ipfs_resolver_get(const char* path, struct Node* from, const struct FSRepo* fs_repo); diff --git a/include/ipfs/merkledag/merkledag.h b/include/ipfs/merkledag/merkledag.h index 189ace1..e2f7ecd 100644 --- a/include/ipfs/merkledag/merkledag.h +++ b/include/ipfs/merkledag/merkledag.h @@ -25,4 +25,13 @@ int ipfs_merkledag_add(struct Node* node, struct FSRepo* fs_repo, size_t* bytes_ */ int ipfs_merkledag_get(const unsigned char* hash, size_t hash_size, struct Node** node, const struct FSRepo* fs_repo); +/*** + * Retrieves a node from the datastore based on the multihash + * @param multihash the base58 encoded multihash (should start with Qm) as a null terminated string + * @param node the node to be created + * @param fs_repo the repository + * @returns true(1) on success + */ +int ipfs_merkledag_get_by_multihash(const unsigned char* multihash, size_t multihash_length, struct Node** node, const struct FSRepo* fs_repo); + #endif diff --git a/include/ipfs/merkledag/node.h b/include/ipfs/merkledag/node.h index e7b2d94..1c81829 100644 --- a/include/ipfs/merkledag/node.h +++ b/include/ipfs/merkledag/node.h @@ -142,6 +142,13 @@ int ipfs_node_new(struct Node** node); */ int ipfs_node_create_directory(struct Node** node); +/*** + * Determine if this node is actually a directory + * @param node the node to examine + * @returns true(1) if this node is a directory. Otherwise, false(0) + */ +int ipfs_node_is_directory(struct Node* node); + /** * sets the Cid into the struct element titled cached * @param node the node to work with diff --git a/main/Makefile b/main/Makefile index 5e2ee2c..a9c5041 100644 --- a/main/Makefile +++ b/main/Makefile @@ -11,8 +11,7 @@ OBJS = main.o \ ../datastore/ds_helper.o \ ../dnslink/dnslink.o \ ../flatfs/flatfs.o \ - ../importer/importer.o \ - ../importer/exporter.o \ + ../importer/importer.o ../importer/exporter.o ../importer/resolver.o \ ../path/path.o \ ../merkledag/merkledag.o ../merkledag/node.o \ ../multibase/multibase.o \ diff --git a/merkledag/merkledag.c b/merkledag/merkledag.c index 7baf71c..cf3db22 100644 --- a/merkledag/merkledag.c +++ b/merkledag/merkledag.c @@ -78,3 +78,13 @@ int ipfs_merkledag_get(const unsigned char* hash, size_t hash_size, struct Node* return 1; } + +int ipfs_merkledag_get_by_multihash(const unsigned char* multihash, size_t multihash_length, struct Node** node, const struct FSRepo* fs_repo) { + // convert to hash + size_t hash_size = 0; + unsigned char* hash = NULL; + if (mh_multihash_digest(multihash, multihash_length, &hash, &hash_size) < 0) { + return 0; + } + return ipfs_merkledag_get(hash, hash_size, node, fs_repo); +} diff --git a/merkledag/node.c b/merkledag/node.c index ac4eeb8..ca289fa 100644 --- a/merkledag/node.c +++ b/merkledag/node.c @@ -385,6 +385,19 @@ int ipfs_node_create_directory(struct Node** node) { return 1; } +int ipfs_node_is_directory(struct Node* node) { + if (node->data_size < 2) { + return 0; + } + struct UnixFS* unix_fs; + if (ipfs_unixfs_protobuf_decode(node->data, node->data_size, &unix_fs) == 0) { + return 0; + } + int retVal = (unix_fs->data_type == UNIXFS_DIRECTORY); + ipfs_unixfs_free(unix_fs); + return retVal; +} + /** * Set the cached struct element * @param node the node to be modified diff --git a/test/Makefile b/test/Makefile index 9b09625..d173ebe 100644 --- a/test/Makefile +++ b/test/Makefile @@ -10,8 +10,7 @@ OBJS = testit.o test_helper.o \ ../core/builder.o \ ../datastore/ds_helper.o \ ../flatfs/flatfs.o \ - ../importer/importer.o \ - ../importer/exporter.o \ + ../importer/importer.o ../importer/exporter.o ../importer/resolver.o \ ../merkledag/merkledag.o ../merkledag/node.o \ ../multibase/multibase.o \ ../os/utils.o \ diff --git a/test/node/test_resolver.h b/test/node/test_resolver.h new file mode 100644 index 0000000..f9fcd90 --- /dev/null +++ b/test/node/test_resolver.h @@ -0,0 +1,34 @@ +#include "ipfs/importer/resolver.h" + +int test_resolver_get() { + // clean out repository + drop_and_build_repository("/Users/JohnJones/.ipfs"); + + int argc = 3; + char* argv[argc]; + argv[0] = "ipfs"; + argv[1] = "add"; + argv[2] = "/Users/JohnJones/ipfstest"; + + ipfs_import_files(argc, (char**)argv); + + struct FSRepo* fs_repo; + ipfs_repo_fsrepo_new("/Users/JohnJones/.ipfs", NULL, &fs_repo); + ipfs_repo_fsrepo_open(fs_repo); + + // find something that is already in the repository + struct Node* result = ipfs_resolver_get("/ipfs/QmbMecmXESf96ZNry7hRuzaRkEBhjqXpoYfPCwgFzVGDzB", NULL, fs_repo); + if (result == NULL) { + return 0; + } + + ipfs_node_free(result); + + // find something by path + result = ipfs_resolver_get("/ipfs/QmWKtXwRg4oL2KaXhvJ3KyGjFE2PVKREwu7qb65V7ficui/hello_world.txt", NULL, fs_repo); + if (result == NULL) { + return 0; + } + + return 1; +} diff --git a/test/testit.c b/test/testit.c index 9d66ced..e385f84 100644 --- a/test/testit.c +++ b/test/testit.c @@ -4,6 +4,7 @@ #include "merkledag/test_merkledag.h" #include "node/test_node.h" #include "node/test_importer.h" +#include "node/test_resolver.h" #include "repo/test_repo_bootstrap_peers.h" #include "repo/test_repo_config.h" #include "repo/test_repo_fsrepo.h" @@ -52,6 +53,7 @@ const char* names[] = { "test_merkledag_get_data", "test_merkledag_add_node", "test_merkledag_add_node_with_links", + "test_resolver_get", "test_unixfs_encode_decode", "test_unixfs_encode_smallfile" }; @@ -85,6 +87,7 @@ int (*funcs[])(void) = { test_merkledag_get_data, test_merkledag_add_node, test_merkledag_add_node_with_links, + test_resolver_get, test_unixfs_encode_decode, test_unixfs_encode_smallfile };