203 lines
6.3 KiB
C
203 lines
6.3 KiB
C
/**
|
|
* a datastore implementation that stores all
|
|
* objects in a 2 level directory structure in
|
|
* the local file system, regardless of the
|
|
* hierarchy of the keys. Modeled after go-ds-flatfs
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "libp2p/os/utils.h"
|
|
|
|
#define FLATFS_MAX_PREFIX_LENGTH 16
|
|
|
|
/**
|
|
* Helper (private) methods
|
|
*/
|
|
|
|
/**
|
|
* remove beginning slash from string
|
|
* @param in the filename to look at
|
|
* @param out a place to store the results
|
|
* @param the size of out
|
|
* @returns true(1) on success
|
|
*/
|
|
int ipfs_flatfs_remove_preceeding_slash(const char* in, char* out, size_t max_size) {
|
|
// make sure max_size is reasonable
|
|
if (max_size < strlen(in) + 1)
|
|
return 0;
|
|
int pos = 0;
|
|
while (in[pos] == '/')
|
|
pos++;
|
|
strncpy(out, &in[pos], strlen(in) - pos);
|
|
out[strlen(in) - pos] = 0;
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Create a directory if it doesn't already exist
|
|
* @param full_directory the full path
|
|
* @returns true(1) on successful create or if it already exists and is writable. false(0) otherwise.
|
|
*/
|
|
int ipfs_flatfs_create_directory(const char* full_directory) {
|
|
// shortcut
|
|
if (os_utils_directory_writeable(full_directory))
|
|
return 1;
|
|
// is it there, just not writeable?
|
|
if (os_utils_directory_exists(full_directory)) {
|
|
return 0;
|
|
}
|
|
// it is not there, create it
|
|
#ifdef __MINGW32__
|
|
if (mkdir(full_directory) == -1)
|
|
return 0;
|
|
#else
|
|
if (mkdir(full_directory, S_IRWXU) == -1)
|
|
return 0;
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
/***
|
|
* public methods
|
|
*/
|
|
|
|
/**
|
|
* Given a filename (usually a long hash), derive a subdirectory name
|
|
* @param datastore_path the path to the datastore
|
|
* @param proposed_filename the filename to use
|
|
* @param derived_path the complete pathname to the directory that should contain the proposed_filename
|
|
* @param max_derived_path_length the maximum memory allocated for derived_path
|
|
* @returns true(1) on success
|
|
*/
|
|
int ipfs_flatfs_get_directory(const char* datastore_path, const char* proposed_filename,
|
|
char* derived_path, size_t max_derived_path_length) {
|
|
// make sure max_derived_path_length is a reasonable number
|
|
if (max_derived_path_length < strlen(datastore_path) + 17)
|
|
return 0;
|
|
|
|
// remove slash prefix if there is one
|
|
char buffer[max_derived_path_length];
|
|
int retVal = ipfs_flatfs_remove_preceeding_slash(proposed_filename, buffer, max_derived_path_length);
|
|
if (retVal == 0)
|
|
return 0;
|
|
|
|
// make it 16 characters
|
|
if (strlen(buffer) < 16) {
|
|
int pos = strlen(buffer);
|
|
int lacking = 16 - pos; // how many we should add
|
|
memset(&buffer[strlen(buffer)], '_', lacking);
|
|
buffer[pos + lacking] = 0;
|
|
}
|
|
// it may be too long, cut it
|
|
if (strlen(buffer) > 16)
|
|
buffer[16] = 0;
|
|
retVal = os_utils_filepath_join(datastore_path, buffer, derived_path, max_derived_path_length);
|
|
return retVal;
|
|
}
|
|
|
|
/**
|
|
* Given the proposed filename, return the acutal filename on the disk (clean the name and add .data suffix)
|
|
* @param proposed_filename the start
|
|
* @param derived_filename the results
|
|
* @param max_derived_filename_length the buffer size
|
|
* @returns true(1) on success
|
|
*/
|
|
int ipfs_flatfs_get_filename(const char* proposed_filename, char* derived_filename, size_t max_derived_filename_length) {
|
|
// get rid of slashes
|
|
char buffer[max_derived_filename_length];
|
|
int retVal = ipfs_flatfs_remove_preceeding_slash(proposed_filename, buffer, max_derived_filename_length);
|
|
if (retVal == 0)
|
|
return 0;
|
|
|
|
// make sure we have space
|
|
if (max_derived_filename_length < strlen(buffer) + 6) // ".data" plus terminating null
|
|
return 0;
|
|
|
|
// add the suffix
|
|
strncat(buffer, ".data", 6);
|
|
|
|
// put it in the result buffer
|
|
strncpy(derived_filename, buffer, strlen(buffer) + 1);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Combines the datastore path, the directory (derived from the filename itself), the proposed
|
|
* filename, and the suffix (.data) to build a complete filename on the disk
|
|
* @param datastore_path where the datastore is
|
|
* @param proposed_filename the filename we want to use
|
|
* @param derived_full_filename where the results will be put
|
|
* @param max_derived_filename_length the size of memory allocated for "derived_full_filename"
|
|
* @returns true(1) on success
|
|
*/
|
|
int ipfs_flatfs_get_full_filename(const char* datastore_path, const char* proposed_filename,
|
|
char* derived_full_filename, size_t max_derived_filename_length) {
|
|
// get rid of preceeding /
|
|
char directory[max_derived_filename_length];
|
|
int retVal = ipfs_flatfs_remove_preceeding_slash(proposed_filename, directory, max_derived_filename_length);
|
|
if (retVal == 0)
|
|
return 0;
|
|
|
|
// start with the path
|
|
retVal = ipfs_flatfs_get_directory(datastore_path, proposed_filename, directory, max_derived_filename_length);
|
|
if (retVal == 0)
|
|
return retVal;
|
|
|
|
// now get the filename
|
|
char actual_filename[max_derived_filename_length];
|
|
retVal = ipfs_flatfs_get_filename(proposed_filename, actual_filename, max_derived_filename_length);
|
|
if (retVal == 0)
|
|
return 0;
|
|
|
|
// now merge the two
|
|
retVal = os_utils_filepath_join(directory, actual_filename, derived_full_filename, max_derived_filename_length);
|
|
|
|
return retVal;
|
|
}
|
|
|
|
|
|
/**
|
|
* Write a file given the key and the contents
|
|
* @param datastore_path the root of the flatfs datastore
|
|
* @param key the "filename"
|
|
* @para byte the contents of the file as a byte array
|
|
* @param num_bytes the length of the byte array
|
|
*/
|
|
int ipfs_flatfs_put(const char* datastore_path, const char* key, unsigned char* byte, size_t num_bytes) {
|
|
size_t filename_length = strlen(datastore_path) + strlen(key) + 24; // subdirectory is 16, 2 slashes, .data suffix, terminating null
|
|
// subdirectory
|
|
char full_filename[filename_length];
|
|
int retVal = ipfs_flatfs_get_directory(datastore_path, key, full_filename, filename_length);
|
|
if (retVal == 0)
|
|
return 0;
|
|
retVal = ipfs_flatfs_create_directory(full_filename);
|
|
if (retVal == 0)
|
|
return 0;
|
|
|
|
// filename
|
|
retVal = ipfs_flatfs_get_full_filename(datastore_path, key, full_filename, filename_length);
|
|
if (retVal == 0)
|
|
return 0;
|
|
|
|
//TODO: Error checking (i.e. too many open files
|
|
|
|
// write temp file
|
|
char temp_filename[filename_length + 5];
|
|
strncpy(temp_filename, full_filename, strlen(full_filename) + 1);
|
|
strcat(temp_filename, ".tmp");
|
|
FILE* out = fopen(temp_filename, "w");
|
|
size_t bytes_written = fwrite(byte, num_bytes, 1, out);
|
|
fclose(out);
|
|
|
|
// rename temp file to real name
|
|
retVal = rename(temp_filename, full_filename);
|
|
if (retVal != 0)
|
|
return 0;
|
|
|
|
return bytes_written == num_bytes;
|
|
}
|