2016-10-27 18:11:34 +00:00
|
|
|
//
|
|
|
|
// fs_repo.c
|
|
|
|
// c-ipfs
|
|
|
|
//
|
|
|
|
// Created by John Jones on 10/27/16.
|
|
|
|
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
|
|
|
//
|
|
|
|
|
2016-11-10 13:28:51 +00:00
|
|
|
#include "libp2p/crypto/encoding/base64.h"
|
|
|
|
|
2016-11-07 21:29:30 +00:00
|
|
|
#include "ipfs/repo/fsrepo/fs_repo.h"
|
2016-10-31 16:13:42 +00:00
|
|
|
#include "ipfs/os/utils.h"
|
2016-10-27 18:11:34 +00:00
|
|
|
/**
|
|
|
|
* private methods
|
|
|
|
*/
|
|
|
|
|
2016-11-03 04:05:29 +00:00
|
|
|
/**
|
|
|
|
* writes the config file
|
|
|
|
* @param full_filename the full filename of the config file in the OS
|
|
|
|
* @param config the details to put into the file
|
|
|
|
* @returns true(1) on success, else false(0)
|
|
|
|
*/
|
|
|
|
int repo_config_write_config_file(char* full_filename, struct RepoConfig* config) {
|
|
|
|
FILE* out_file = fopen(full_filename, "w");
|
|
|
|
if (out_file == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
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
|
2016-11-10 13:28:51 +00:00
|
|
|
// first base 64 it
|
|
|
|
size_t encoded_size = base64_encode_length(config->identity.private_key.der, config->identity.private_key.der_length);
|
|
|
|
unsigned char encoded_buffer[encoded_size + 1];
|
|
|
|
int retVal = base64_encode(config->identity.private_key.der, config->identity.private_key.der_length, encoded_buffer, encoded_size, &encoded_size);
|
|
|
|
if (retVal == 0)
|
|
|
|
return 0;
|
|
|
|
encoded_buffer[encoded_size] = 0;
|
|
|
|
fprintf(out_file, " \"PrivKey\": \"%s\"\n", encoded_buffer);
|
2016-11-03 04:05:29 +00:00
|
|
|
fprintf(out_file, " },\n");
|
|
|
|
fprintf(out_file, " \"Datastore\": {\n");
|
|
|
|
fprintf(out_file, " \"Type\": \"%s\",\n", config->datastore.type);
|
|
|
|
fprintf(out_file, " \"Path\": \"%s\",\n", config->datastore.path);
|
|
|
|
fprintf(out_file, " \"StorageMax\": \"%s\",\n", config->datastore.storage_max);
|
|
|
|
fprintf(out_file, " \"StorageGCWatermark\": %d,\n", config->datastore.storage_gc_watermark);
|
|
|
|
fprintf(out_file, " \"GCPeriod\": \"%s\",\n", config->datastore.gc_period);
|
|
|
|
fprintf(out_file, " \"Params\": null,\n");
|
|
|
|
fprintf(out_file, " \"NoSync\": %s,\n", config->datastore.no_sync ? "true" : "false");
|
|
|
|
fprintf(out_file, " \"HashOnRead\": %s,\n", config->datastore.hash_on_read ? "true" : "false");
|
|
|
|
fprintf(out_file, " \"BloomFilterSize\": %d\n", config->datastore.bloom_filter_size);
|
|
|
|
fprintf(out_file, " },\n \"Addresses\": {\n");
|
|
|
|
fprintf(out_file, " \"Swarm\": [\n");
|
|
|
|
for(int i = 0; i < config->addresses.swarm.num_addresses; i++) {
|
|
|
|
fprintf(out_file, " \"%s\"", config->addresses.swarm.addresses[i]);
|
|
|
|
if (i != (config->addresses.swarm.num_addresses - 1))
|
|
|
|
fprintf(out_file, ",\n");
|
|
|
|
else
|
|
|
|
fprintf(out_file, "\n");
|
|
|
|
}
|
|
|
|
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, " \"IPFS\": \"%s\",\n", config->mounts.ipfs);
|
|
|
|
fprintf(out_file, " \"IPNS\": \"%s\",\n", config->mounts.ipns);
|
|
|
|
fprintf(out_file, " \"FuseAllowOther\": %s\n", "false");
|
|
|
|
fprintf(out_file, " },\n \"Discovery\": {\n \"MDNS\": {\n");
|
|
|
|
fprintf(out_file, " \"Enabled\": %s,\n", config->discovery.mdns.enabled ? "true" : "false");
|
|
|
|
fprintf(out_file, " \"Interval\": %d\n }\n },\n", config->discovery.mdns.interval);
|
|
|
|
fprintf(out_file, " \"Ipns\": {\n");
|
|
|
|
fprintf(out_file, " \"RepublishedPeriod\": \"\",\n");
|
|
|
|
fprintf(out_file, " \"RecordLifetime\": \"\",\n");
|
|
|
|
fprintf(out_file, " \"ResolveCacheSize\": %d\n", config->ipns.resolve_cache_size);
|
|
|
|
fprintf(out_file, " },\n \"Bootstrap\": [\n");
|
|
|
|
for(int i = 0; i < config->peer_addresses.num_peers; i++) {
|
|
|
|
fprintf(out_file, " \"%s\"", config->peer_addresses.peers[i]->entire_string);
|
|
|
|
if (i < config->peer_addresses.num_peers - 1)
|
|
|
|
fprintf(out_file, ",\n");
|
|
|
|
else
|
|
|
|
fprintf(out_file, "\n");
|
|
|
|
}
|
|
|
|
fprintf(out_file, " ],\n \"Tour\": {\n \"Last\": \"\"\n },\n");
|
|
|
|
fprintf(out_file, " \"Gateway\": {\n");
|
|
|
|
fprintf(out_file, " \"HTTPHeaders\": {\n");
|
|
|
|
for (int i = 0; i < config->gateway.http_headers.num_elements; i++) {
|
|
|
|
fprintf(out_file, " \"%s\": [\n \"%s\"\n ]", config->gateway.http_headers.headers[i]->header, config->gateway.http_headers.headers[i]->value);
|
|
|
|
if (i < config->gateway.http_headers.num_elements - 1)
|
|
|
|
fprintf(out_file, ",\n");
|
|
|
|
else
|
|
|
|
fprintf(out_file, "\n");
|
|
|
|
}
|
|
|
|
fprintf(out_file, " \"RootRedirect\": \"%s\"\n", config->gateway.root_redirect);
|
|
|
|
fprintf(out_file, " \"Writable\": %s\n", config->gateway.writable ? "true" : "false");
|
|
|
|
fprintf(out_file, " \"PathPrefixes\": []\n");
|
|
|
|
fprintf(out_file, " },\n \"SupernodeRouting\": {\n");
|
|
|
|
fprintf(out_file, " \"Servers\": null\n },");
|
|
|
|
fprintf(out_file, " \"API\": {\n \"HTTPHeaders\": null\n },\n");
|
|
|
|
fprintf(out_file, " \"Swarm\": {\n \"AddrFilters\": null\n }\n}");
|
|
|
|
fclose(out_file);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-10-27 18:11:34 +00:00
|
|
|
/**
|
|
|
|
* constructs the FSRepo struct. Basically fills in the FSRepo.path
|
2016-10-31 16:13:42 +00:00
|
|
|
* Remember: the path must be freed
|
2016-10-27 18:11:34 +00:00
|
|
|
* @param repo_path the path to the repo
|
|
|
|
* @param repo the struct to fill in
|
|
|
|
* @returns false(0) if something bad happened, otherwise true(1)
|
|
|
|
*/
|
2016-11-10 13:28:51 +00:00
|
|
|
int fs_repo_new_fs_repo(char* repo_path, struct FSRepo* repo) {
|
|
|
|
if (repo_path == NULL) {
|
|
|
|
// get the user's home directory
|
|
|
|
char* home_dir = os_utils_get_homedir();
|
|
|
|
char* default_subdir = "/.ipfs";
|
|
|
|
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;
|
|
|
|
} else {
|
|
|
|
int len = strlen(repo_path) + 1;
|
|
|
|
repo->path = (char*)malloc(len);
|
|
|
|
strncpy(repo->path, repo_path, len);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Cleans up memory
|
|
|
|
* @param repo the struct to clean up
|
|
|
|
* @returns true(1) on success
|
|
|
|
*/
|
|
|
|
int fs_repo_free(struct FSRepo* repo) {
|
|
|
|
free(repo->path);
|
2016-10-27 18:11:34 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* checks to see if the repo is initialized at the given path
|
|
|
|
* @param full_path the path to the repo
|
|
|
|
* @returns true(1) if the config file is there, false(0) otherwise
|
|
|
|
*/
|
|
|
|
int repo_config_is_initialized(char* full_path) {
|
|
|
|
char* config_file_full_path;
|
2016-11-03 04:05:29 +00:00
|
|
|
int retVal = repo_config_get_file_name(full_path, &config_file_full_path);
|
2016-10-27 18:11:34 +00:00
|
|
|
if (!retVal)
|
|
|
|
return 0;
|
2016-11-03 04:05:29 +00:00
|
|
|
|
2016-10-27 18:11:34 +00:00
|
|
|
if (os_utils_file_exists(config_file_full_path))
|
|
|
|
retVal = 1;
|
2016-11-03 04:05:29 +00:00
|
|
|
else
|
|
|
|
retVal = 0;
|
|
|
|
|
|
|
|
free(config_file_full_path);
|
2016-10-27 18:11:34 +00:00
|
|
|
return retVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
|
|
* Check to see if the repo is initialized
|
|
|
|
* @param full_path the path to the repo
|
|
|
|
* @returns true(1) if it is initialized, false(0) otherwise.
|
|
|
|
*/
|
2016-10-31 16:13:42 +00:00
|
|
|
int fs_repo_is_initialized_unsynced(char* full_path) {
|
2016-10-27 18:11:34 +00:00
|
|
|
return repo_config_is_initialized(full_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* checks to see if the repo is initialized
|
|
|
|
* @param full_path the full path to the repo
|
|
|
|
* @returns true(1) if it is initialized, otherwise false(0)
|
|
|
|
*/
|
|
|
|
int repo_check_initialized(char* full_path) {
|
|
|
|
// note the old version of this reported an error if the repo was a .go-ipfs repo (by looking at the path)
|
|
|
|
// this version skips that step
|
2016-10-31 16:13:42 +00:00
|
|
|
return fs_repo_is_initialized_unsynced(full_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
|
|
* opens the datastore and puts it in 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
|
2016-11-10 13:28:51 +00:00
|
|
|
return 1;
|
2016-10-31 16:13:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
|
|
* 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) {
|
2016-11-10 13:28:51 +00:00
|
|
|
//TODO: this
|
|
|
|
return 1;
|
2016-10-27 18:11:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* public methods
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* opens a fsrepo
|
|
|
|
* @param repo_path the path to the repo
|
|
|
|
* @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) {
|
|
|
|
//TODO: lock
|
2016-10-31 16:13:42 +00:00
|
|
|
// get the path set in the repo struct
|
2016-11-10 13:28:51 +00:00
|
|
|
int retVal = fs_repo_new_fs_repo(repo_path, repo);
|
|
|
|
if (retVal == 0) {
|
|
|
|
fs_repo_free(repo);
|
2016-11-02 18:44:56 +00:00
|
|
|
return 0;
|
2016-11-10 13:28:51 +00:00
|
|
|
}
|
2016-11-02 18:44:56 +00:00
|
|
|
|
2016-10-27 18:11:34 +00:00
|
|
|
// check if initialized
|
2016-11-10 13:28:51 +00:00
|
|
|
if (!repo_check_initialized(repo->path)) {
|
|
|
|
fs_repo_free(repo);
|
2016-10-27 18:11:34 +00:00
|
|
|
return 0;
|
2016-11-10 13:28:51 +00:00
|
|
|
}
|
2016-10-31 16:13:42 +00:00
|
|
|
//TODO: lock the file (remember to unlock)
|
|
|
|
//TODO: check the version, and make sure it is correct
|
|
|
|
//TODO: make sure the directory is writable
|
|
|
|
//TODO: open the config
|
2016-11-10 13:28:51 +00:00
|
|
|
if (!fs_repo_open_config(repo)) {
|
|
|
|
fs_repo_free(repo);
|
|
|
|
return 0;
|
|
|
|
}
|
2016-10-31 16:13:42 +00:00
|
|
|
//TODO: open the datastore. Note: the config file has the datastore type
|
2016-11-10 13:28:51 +00:00
|
|
|
if (!fs_repo_open_datastore(repo)) {
|
|
|
|
fs_repo_free(repo);
|
|
|
|
return 0;
|
|
|
|
}
|
2016-10-31 16:13:42 +00:00
|
|
|
|
2016-11-10 13:28:51 +00:00
|
|
|
return 1;
|
2016-10-27 18:11:34 +00:00
|
|
|
}
|
|
|
|
|
2016-10-31 16:13:42 +00:00
|
|
|
/***
|
|
|
|
* checks to see if the repo is initialized
|
|
|
|
* @param repo_path the path to the repo
|
|
|
|
* @returns true(1) if it is initialized, otherwise false(0)
|
|
|
|
*/
|
|
|
|
int fs_repo_is_initialized(char* repo_path) {
|
|
|
|
//TODO: lock things up so that someone doesn't try an init or remove while this call is in progress
|
|
|
|
// don't forget to unlock
|
|
|
|
return fs_repo_is_initialized_unsynced(repo_path);
|
|
|
|
}
|
|
|
|
|
2016-11-10 13:28:51 +00:00
|
|
|
/**
|
|
|
|
* Initializes a new FSRepo at the given path with the provided config
|
|
|
|
* @param path the path to use
|
|
|
|
* @param config the information for the config file
|
|
|
|
* @returns true(1) on success
|
|
|
|
*/
|
2016-11-03 04:05:29 +00:00
|
|
|
int fs_repo_init(char* path, struct RepoConfig* config) {
|
|
|
|
// 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))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int retVal = fs_repo_write_config_file(path, config);
|
|
|
|
if (retVal == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// TODO: Implement this method
|
2016-11-07 21:29:30 +00:00
|
|
|
//retVal = fs_repo_defaultds_init(path, config);
|
2016-11-03 04:05:29 +00:00
|
|
|
if (retVal == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// write the version to a file for migrations (see repo/fsrepo/migrations/mfsr.go)
|
|
|
|
//TODO: mfsr.RepoPath(repo_path).WriteVersion(RepoVersion)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* write the config file to disk
|
|
|
|
* @param path the path to the file
|
|
|
|
* @param config the config structure
|
|
|
|
* @returns true(1) on success
|
|
|
|
*/
|
|
|
|
int fs_repo_write_config_file(char* path, struct RepoConfig* config) {
|
|
|
|
if (fs_repo_is_initialized(path))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
char* buff = NULL;
|
|
|
|
if (!repo_config_get_file_name(path, &buff))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int retVal = repo_config_write_config_file(buff, config);
|
|
|
|
|
|
|
|
free(buff);
|
|
|
|
|
|
|
|
return retVal;
|
|
|
|
}
|
|
|
|
|