From fa78e23533c2a75f4deb61a90bd20a9065a31913 Mon Sep 17 00:00:00 2001 From: Falk Werner <47070255+falk-werner@users.noreply.github.com> Date: Fri, 26 Apr 2019 20:49:09 +0200 Subject: [PATCH] feat(webfuse): static file provider (#30) * added API stub of static_filesystem * added callback functions * added basic directory listing * resize filesystem if necessary * added path stub * adds imlementation and tests of path * adds mock of wpf_request * adds test implementation and some matchers * added matcher of readdir results * fixes default directory test * adds implementation of static_filesystem_add and add_text * added implementation of read * adds implementation of filesystem_read * corrects naming of some functions * removes Flawfinder comments, since Flawfinder is disabled --- CMakeLists.txt | 17 +- example/lib/userdb/src/userdb.c | 6 +- example/provider/static_filesystem.c | 99 ++++ include/webfuse/provider/static_filesystem.h | 76 +++ include/webfuse_provider.h | 2 + lib/webfuse/adapter/impl/operation/read.c | 2 +- lib/webfuse/core/path.c | 116 ++++ lib/webfuse/core/path.h | 43 ++ lib/webfuse/core/string.c | 4 +- lib/webfuse/provider/api.c | 57 ++ lib/webfuse/provider/impl/operation/open.c | 2 +- lib/webfuse/provider/impl/operation/read.c | 2 +- lib/webfuse/provider/impl/static_filesystem.c | 502 ++++++++++++++++++ lib/webfuse/provider/impl/static_filesystem.h | 52 ++ test/json_matcher.hpp | 34 ++ test/mock_request.cc | 55 ++ test/mock_request.hpp | 122 +++++ test/test_path.cc | 58 ++ test/test_static_filesystem.cc | 61 +++ 19 files changed, 1301 insertions(+), 9 deletions(-) create mode 100644 example/provider/static_filesystem.c create mode 100644 include/webfuse/provider/static_filesystem.h create mode 100644 lib/webfuse/core/path.c create mode 100644 lib/webfuse/core/path.h create mode 100644 lib/webfuse/provider/impl/static_filesystem.c create mode 100644 lib/webfuse/provider/impl/static_filesystem.h create mode 100644 test/json_matcher.hpp create mode 100644 test/mock_request.cc create mode 100644 test/mock_request.hpp create mode 100644 test/test_path.cc create mode 100644 test/test_static_filesystem.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 774eca6..de24dd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,7 +56,8 @@ add_library(webfuse-core STATIC lib/webfuse/core/message.c lib/webfuse/core/message_queue.c lib/webfuse/core/status.c - lib/webfuse/core/string.c + lib/webfuse/core/string.c + lib/webfuse/core/path.c ) set_target_properties(webfuse-core PROPERTIES OUTPUT_NAME webfuse-core) @@ -152,6 +153,7 @@ add_library(webfuse-provider-static STATIC lib/webfuse/provider/impl/operation/open.c lib/webfuse/provider/impl/operation/close.c lib/webfuse/provider/impl/operation/read.c + lib/webfuse/provider/impl/static_filesystem.c ) set_target_properties(webfuse-provider-static PROPERTIES OUTPUT_NAME webfuse-provider) @@ -237,6 +239,16 @@ target_link_libraries(webfuse-provider-app PUBLIC webfuse-provider ${EXTRA_LIBS} target_include_directories(webfuse-provider-app PUBLIC ${EXTRA_INCLUDE_DIRS}) target_compile_options(webfuse-provider-app PUBLIC ${EXTRA_CFLAGS}) +# static-filesystem-provider + +add_executable(static-filesystem-provider + example/provider/static_filesystem.c +) + +target_link_libraries(static-filesystem-provider PUBLIC webfuse-provider ${EXTRA_LIBS}) +target_include_directories(static-filesystem-provider PUBLIC ${EXTRA_INCLUDE_DIRS}) +target_compile_options(static-filesystem-provider PUBLIC ${EXTRA_CFLAGS}) + # webfuse-passwd add_executable(webfuse-passwd @@ -278,6 +290,7 @@ pkg_check_modules(GMOCK gmock) add_executable(alltests test/msleep.cc test/mock_authenticator.cc + test/mock_request.cc test/test_container_of.cc test/test_response_parser.cc test/test_server.cc @@ -289,6 +302,8 @@ add_executable(alltests test/test_authenticators.cc test/test_string.cc test/test_slist.cc + test/test_path.cc + test/test_static_filesystem.cc ) target_link_libraries(alltests PUBLIC webfuse-adapter-static webfuse-provider-static webfuse-core ${EXTRA_LIBS} ${GMOCK_LIBRARIES} ${GTEST_LIBRARIES}) diff --git a/example/lib/userdb/src/userdb.c b/example/lib/userdb/src/userdb.c index 8e5877a..172742a 100644 --- a/example/lib/userdb/src/userdb.c +++ b/example/lib/userdb/src/userdb.c @@ -127,9 +127,9 @@ static char * compute_hash( { EVP_MD_CTX * context = EVP_MD_CTX_new(); EVP_DigestInit_ex(context, digest, NULL); - EVP_DigestUpdate(context, password, strlen(password)); /* Flawfinder: ignore */ - EVP_DigestUpdate(context, salt, strlen(salt)); /* Flawfinder: ignore */ - EVP_DigestUpdate(context, db->pepper, strlen(db->pepper)); /* Flawfinder: ignore */ + EVP_DigestUpdate(context, password, strlen(password)); + EVP_DigestUpdate(context, salt, strlen(salt)); + EVP_DigestUpdate(context, db->pepper, strlen(db->pepper)); EVP_DigestFinal_ex(context, hash, &hash_size); EVP_MD_CTX_free(context); diff --git a/example/provider/static_filesystem.c b/example/provider/static_filesystem.c new file mode 100644 index 0000000..03801f8 --- /dev/null +++ b/example/provider/static_filesystem.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include + +#include "webfuse_provider.h" + +struct args +{ + char const * url; + bool show_help; +}; + +static int +parse_args( + struct args * args, + int argc, + char * argv[]) +{ + int result = EXIT_FAILURE; + args->show_help = true; + args->url = NULL; + + if (2 == argc) + { + result = EXIT_SUCCESS; + + char const * url = argv[1]; + if ((0 != strcmp(url, "-h")) && (0 != strcmp(url, "--help"))) + { + args->show_help = false; + args->url = url; + } + } + else + { + fprintf(stderr, "error: missing argument\n"); + } + + return result; +} + +static struct wfp_client * client = NULL; + +static void on_interrupt(int signal_id) +{ + (void) signal_id; + + wfp_client_shutdown(client); +} + +static void print_usage() +{ + printf( + "static-filesystem-provider Copyright (c) 2019, webfuse authors \n" + "Example of webfuse static filesystem provider\n" + "\n" + "Usage: static-filesystem-provider \n" + "\n" + "Arguments:\n" + "\t URL of webfuse server (required)\n" + "\t-h, --help prints this message\n" + "\n" + "Example:\n" + "\tstatic-filesystem-provider ws://localhost:8080/\n" + "\n" + ); +} + +int main(int argc, char* argv[]) +{ + signal(SIGINT, &on_interrupt); + + struct args args; + int result = parse_args(&args, argc, argv); + if (EXIT_SUCCESS == result) + { + struct wfp_client_config * config = wfp_client_config_create(); + + struct wfp_static_filesystem * fs = wfp_static_filesystem_create(config); + wfp_static_filesystem_add_text(fs, "hello.txt", 0444, "Hello, World!"); + + client = wfp_client_create(config); + wfp_client_connect(client, args.url); + wfp_client_run(client); + + wfp_client_dispose(client); + wfp_static_filesystem_dispose(fs); + wfp_client_config_dispose(config); + } + + if (args.show_help) + { + print_usage(); + } + + return result; +} diff --git a/include/webfuse/provider/static_filesystem.h b/include/webfuse/provider/static_filesystem.h new file mode 100644 index 0000000..b2bdbfc --- /dev/null +++ b/include/webfuse/provider/static_filesystem.h @@ -0,0 +1,76 @@ +#ifndef WFP_STATIC_FILESYSTEM_H +#define WFP_STATIC_FILESYSTEM_H + +#ifndef __cplusplus +#include +#else +#include +using ::std::size_t; +#endif + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfp_client_config; +struct wfp_static_filesystem; + +typedef size_t +wfp_static_filesystem_read_fn( + size_t offset, + char * buffer, + size_t buffer_size, + void * user_data); + +typedef void +wfp_static_filesystem_get_info_fn( + void * user_data, + int * result_mode, + size_t * result_size); + + +extern WFP_API struct wfp_static_filesystem * +wfp_static_filesystem_create( + struct wfp_client_config * config); + +extern WFP_API void +wfp_static_filesystem_dispose( + struct wfp_static_filesystem * filesystem); + +extern WFP_API void +wfp_static_filesystem_add( + struct wfp_static_filesystem * filesystem, + char const * path, + int mode, + char const * content, + size_t length); + +extern WFP_API void +wfp_static_filesystem_add_text( + struct wfp_static_filesystem * filesystem, + char const * path, + int mode, + char const * content); + +extern WFP_API void +wfp_static_filesystem_add_file( + struct wfp_static_filesystem * filesystem, + char const * path, + char const * filename); + +extern WFP_API void +wfp_static_filesystem_add_generic( + struct wfp_static_filesystem * filesystem, + char const * path, + wfp_static_filesystem_read_fn * read, + wfp_static_filesystem_get_info_fn * get_info, + void * user_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/webfuse_provider.h b/include/webfuse_provider.h index 26398ff..e37fd68 100644 --- a/include/webfuse_provider.h +++ b/include/webfuse_provider.h @@ -17,4 +17,6 @@ #include #include +#include + #endif diff --git a/lib/webfuse/adapter/impl/operation/read.c b/lib/webfuse/adapter/impl/operation/read.c index ea89eff..4022a99 100644 --- a/lib/webfuse/adapter/impl/operation/read.c +++ b/lib/webfuse/adapter/impl/operation/read.c @@ -23,7 +23,7 @@ static char * wf_impl_fill_buffer( { if (0 == strcmp("identity", format)) { - memcpy(buffer, data, count); /* Flawfinder: ignore */ + memcpy(buffer, data, count); } else if (0 == strcmp("base64", format)) { diff --git a/lib/webfuse/core/path.c b/lib/webfuse/core/path.c new file mode 100644 index 0000000..36689f0 --- /dev/null +++ b/lib/webfuse/core/path.c @@ -0,0 +1,116 @@ +#include "webfuse/core/path.h" +#include +#include + +#define WF_PATH_DEFAULT_CAPACITY (8) + +struct wf_path +{ + char * * elements; + size_t count; + size_t capacity; +}; + +static void +wf_path_add( + struct wf_path * path, + char const * element, + size_t element_size) +{ + if (0 < element_size) + { + if (path->count >= path->capacity) + { + size_t new_capacity = 2 * path->capacity; + size_t new_size = sizeof(char*) * new_capacity; + + char * * elements = realloc(path->elements, new_size); + if (NULL != elements) + { + path->elements = elements; + path->capacity = new_capacity; + } + } + + if (path->count < path->capacity) + { + path->elements[path->count] = strndup(element, element_size); + path->count++; + } + } +} + +struct wf_path * +wf_path_create( + char const * value) +{ + struct wf_path * path = malloc(sizeof(struct wf_path)); + if (NULL != path) + { + path->elements = malloc(sizeof(char*) * WF_PATH_DEFAULT_CAPACITY); + path->capacity = WF_PATH_DEFAULT_CAPACITY; + path->count = 0; + + char const * remainder = value; + char const * pos = strchr(remainder, '/'); + while (NULL != pos) + { + wf_path_add(path, remainder, (pos - remainder)); + remainder = pos + 1; + pos = strchr(remainder, '/'); + } + + wf_path_add(path, remainder, strlen(remainder)); + } + + return path; +} + +void +wf_path_dispose( + struct wf_path * path) +{ + for(size_t i = 0; i < path->count; i++) + { + free(path->elements[i]); + } + + free(path->elements); + free(path); + (void) path; +} + +size_t +wf_path_element_count( + struct wf_path * path) +{ + return path->count; +} + +char const * +wf_path_get_element( + struct wf_path * path, + size_t i) +{ + char const * result = NULL; + if (i < path->count) + { + result = path->elements[i]; + } + + return result; +} + +char const * +wf_path_get_filename( + struct wf_path * path) +{ + char const * result = NULL; + + if (0 < path->count) + { + result = path->elements[path->count - 1]; + } + + return result; +} diff --git a/lib/webfuse/core/path.h b/lib/webfuse/core/path.h new file mode 100644 index 0000000..ca31725 --- /dev/null +++ b/lib/webfuse/core/path.h @@ -0,0 +1,43 @@ +#ifndef WF_PATH_H +#define WF_PATH_H + +#ifndef __cplusplus +#include +#else +#include +using ::std::size_t; +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_path; + +extern struct wf_path * +wf_path_create( + char const * value); + +extern void +wf_path_dispose( + struct wf_path * path); + +extern size_t +wf_path_element_count( + struct wf_path * path); + +extern char const * +wf_path_get_element( + struct wf_path * path, + size_t i); + +extern char const * +wf_path_get_filename( + struct wf_path * path); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/core/string.c b/lib/webfuse/core/string.c index b5ead74..c5117eb 100644 --- a/lib/webfuse/core/string.c +++ b/lib/webfuse/core/string.c @@ -10,7 +10,7 @@ char * wf_create_string(char const * format, ...) va_list measure_args; va_start(measure_args, format); char buffer; - int needed = vsnprintf(&buffer, 1, format, measure_args); /* Flawfinder: ignore */ + int needed = vsnprintf(&buffer, 1, format, measure_args); va_end(measure_args); if (0 <= needed) @@ -20,7 +20,7 @@ char * wf_create_string(char const * format, ...) { va_list args; va_start(args, format); - int count = vsnprintf(result, needed + 1, format, args); /* Flawfinder: ignore */ + int count = vsnprintf(result, needed + 1, format, args); va_end(args); if ((count < 0) || (needed < count)) diff --git a/lib/webfuse/provider/api.c b/lib/webfuse/provider/api.c index 1384d20..0d9a96b 100644 --- a/lib/webfuse/provider/api.c +++ b/lib/webfuse/provider/api.c @@ -11,6 +11,7 @@ #include "webfuse/provider/impl/client_config.h" #include "webfuse/provider/impl/client.h" #include "webfuse/provider/impl/dirbuffer.h" +#include "webfuse/provider/impl/static_filesystem.h" // respond @@ -238,3 +239,59 @@ void wfp_dirbuffer_add( wfp_impl_dirbuffer_add(buffer, name, inode); } +// static_filesystem + +struct wfp_static_filesystem * +wfp_static_filesystem_create( + struct wfp_client_config * config) +{ + return wfp_impl_static_filesystem_create(config); +} + +void +wfp_static_filesystem_dispose( + struct wfp_static_filesystem * filesystem) +{ + wfp_impl_static_filesystem_dispose(filesystem); +} + +void +wfp_static_filesystem_add( + struct wfp_static_filesystem * filesystem, + char const * path, + int mode, + char const * content, + size_t length) +{ + wfp_impl_static_filesystem_add(filesystem, path, mode, content, length); +} + +void +wfp_static_filesystem_add_text( + struct wfp_static_filesystem * filesystem, + char const * path, + int mode, + char const * content) +{ + wfp_impl_static_filesystem_add_text(filesystem, path, mode, content); +} + +void +wfp_static_filesystem_add_file( + struct wfp_static_filesystem * filesystem, + char const * path, + char const * filename) +{ + wfp_impl_static_filesystem_add_file(filesystem, path, filename); +} + +void +wfp_static_filesystem_add_generic( + struct wfp_static_filesystem * filesystem, + char const * path, + wfp_static_filesystem_read_fn * read, + wfp_static_filesystem_get_info_fn * get_info, + void * user_data) +{ + wfp_impl_static_filesystem_add_generic(filesystem, path, read, get_info, user_data); +} diff --git a/lib/webfuse/provider/impl/operation/open.c b/lib/webfuse/provider/impl/operation/open.c index 25c8dff..a0ed9b1 100644 --- a/lib/webfuse/provider/impl/operation/open.c +++ b/lib/webfuse/provider/impl/operation/open.c @@ -22,7 +22,7 @@ void wfp_impl_open( struct wfp_request * request = wfp_impl_request_create(context->request, id); - context->provider->open(request, inode, flags, context->user_data); /* Flawfinder: ignore */ + context->provider->open(request, inode, flags, context->user_data); } } } diff --git a/lib/webfuse/provider/impl/operation/read.c b/lib/webfuse/provider/impl/operation/read.c index 19a9926..64188b4 100644 --- a/lib/webfuse/provider/impl/operation/read.c +++ b/lib/webfuse/provider/impl/operation/read.c @@ -31,7 +31,7 @@ void wfp_impl_read( size_t length = json_integer_value(length_holder); struct wfp_request * request = wfp_impl_request_create(context->request, id); - context->provider->read(request, inode, handle, offset, length, context->user_data); /* Flawfinder: ignore */ + context->provider->read(request, inode, handle, offset, length, context->user_data); } } } diff --git a/lib/webfuse/provider/impl/static_filesystem.c b/lib/webfuse/provider/impl/static_filesystem.c new file mode 100644 index 0000000..f378f3d --- /dev/null +++ b/lib/webfuse/provider/impl/static_filesystem.c @@ -0,0 +1,502 @@ +#include "webfuse/provider/impl/static_filesystem.h" +#include "webfuse/provider/client_config.h" +#include "webfuse/provider/dirbuffer.h" +#include "webfuse/provider/operation/error.h" + +#include "webfuse/core/path.h" +#include "webfuse/core/util.h" + +#include +#include +#include + +#include +#include +#include +#include + +#define WFP_STATIC_FILESYSTEM_DEFAULT_CAPACITY (16) +#define WFP_STATIC_FILSYSTEM_INDOE_ROOT (1) +#define WFP_STATIC_FILESYSTEM_MAX_READ_SIZE (4 * 1024) + +struct wfp_static_filesystem_entry +{ + size_t inode; + size_t parent; + char * name; + bool is_file; + int mode; + size_t size; + char * content; + wfp_static_filesystem_read_fn * read; + wfp_static_filesystem_get_info_fn * get_info; + void * user_data; +}; + +struct wfp_static_filesystem +{ + struct wfp_static_filesystem_entry * entries; + size_t size; + size_t capacity; +}; + +static struct wfp_static_filesystem_entry * +wfp_impl_static_filesystem_get_entry( + struct wfp_static_filesystem * filesystem, + size_t inode) +{ + struct wfp_static_filesystem_entry * entry = NULL; + + if ((0 < inode) && (inode <= filesystem->size)) + { + entry = &filesystem->entries[inode - 1]; + } + + return entry; +} + +static struct wfp_static_filesystem_entry * +wfp_impl_static_filesystem_get_entry_by_name( + struct wfp_static_filesystem * filesystem, + size_t parent, + char const * name) +{ + struct wfp_static_filesystem_entry * entry = NULL; + for(size_t i = 0; i < filesystem->size; i++) + { + struct wfp_static_filesystem_entry * current = &filesystem->entries[i]; + if ((parent == current->parent) && (0 == strcmp(name, current->name))) + { + entry = current; + break; + } + } + + return entry; +} + +static struct wfp_static_filesystem_entry * +wfp_impl_static_filesystem_add_entry( + struct wfp_static_filesystem * filesystem) +{ + struct wfp_static_filesystem_entry * entry = NULL; + + if (filesystem->size >= filesystem->capacity) + { + struct wfp_static_filesystem_entry * entries; + + size_t new_capacity = 2 * filesystem->capacity; + size_t new_size = new_capacity * sizeof(struct wfp_static_filesystem_entry); + entries = realloc(filesystem->entries, new_size); + + if (NULL != entries) + { + filesystem->entries = entries; + filesystem->capacity = new_capacity; + } + } + + if (filesystem->size < filesystem->capacity) + { + entry = &filesystem->entries[filesystem->size]; + entry->inode = filesystem->size + 1; + filesystem->size++; + } + + return entry; +} + +static size_t +wfp_impl_static_filesystem_entry_read( + size_t offset, + char * buffer, + size_t buffer_size, + void * user_data) +{ + size_t result = 0; + struct wfp_static_filesystem_entry * entry = user_data; + if (offset < entry->size) + { + size_t remaining = (entry->size - offset); + result = (buffer_size < remaining) ? buffer_size : remaining; + memcpy(buffer, entry->content, result); + } + + return result; +} + +static void +wfp_impl_static_filesystem_entry_get_info( + void * user_data, + int * result_mode, + size_t * result_size) +{ + struct wfp_static_filesystem_entry * entry = user_data; + *result_mode = entry->mode; + *result_size = entry->size; +} + +static size_t +wfp_impl_static_filesystem_file_read( + size_t offset, + char * buffer, + size_t buffer_size, + void * user_data) +{ + size_t result = 0; + struct wfp_static_filesystem_entry * entry = user_data; + char const * filename = entry->content; + + FILE * file = fopen(filename, "rb"); + if (NULL != file) + { + fseek(file, offset, SEEK_SET); + result = fread(buffer, buffer_size, 1, file); + fclose(file); + } + + return result; +} + +static void +wfp_impl_static_filesystem_file_get_info( + void * user_data, + int * result_mode, + size_t * result_size) +{ + struct wfp_static_filesystem_entry * entry = user_data; + char const * filename = entry->content; + + struct stat buffer; + stat(filename, &buffer); + + *result_mode = (int) (buffer.st_mode & 0777); + *result_size = (size_t) buffer.st_size; +} + + +static size_t +wfp_impl_static_filesystem_add_dir( + struct wfp_static_filesystem * filesystem, + size_t parent, + char const * name +) +{ + size_t result = 0; + + struct wfp_static_filesystem_entry * entry = wfp_impl_static_filesystem_get_entry_by_name(filesystem, parent, name); + if (NULL == entry) + { + entry = wfp_impl_static_filesystem_add_entry(filesystem); + entry->parent = parent; + entry->is_file = false; + entry->mode = 0555; + entry->name = strdup(name); + entry->user_data = entry; + entry->read = &wfp_impl_static_filesystem_entry_read; + entry->get_info = &wfp_impl_static_filesystem_entry_get_info; + entry->size = 0; + entry->content = NULL; + + result = entry->inode; + } + + return result; +} + +static size_t +wfp_impl_static_filesystem_make_parent( + struct wfp_static_filesystem * filesystem, + struct wf_path * path) +{ + size_t result = WFP_STATIC_FILSYSTEM_INDOE_ROOT; + + size_t count = wf_path_element_count(path); + if (0 < count) + { + for(size_t i = 0; i < (count - 1); i++) + { + char const * name = wf_path_get_element(path, i); + result = wfp_impl_static_filesystem_add_dir(filesystem, result, name); + } + } + + return result; +} + +static void +wfp_impl_static_filesystem_stat( + struct wfp_static_filesystem_entry * entry, + struct stat * stat +) +{ + memset(stat, 0, sizeof(struct stat)); + + int mode; + size_t size; + entry->get_info(entry->user_data, &mode, &size); + + stat->st_ino = entry->inode; + stat->st_size = entry->size; + stat->st_mode = entry->mode & 0777; + stat->st_mode |= (entry->is_file) ? S_IFREG: S_IFDIR; +} + +static void wfp_impl_static_filesystem_lookup( + struct wfp_request * request, + ino_t parent, + char const * name, + void * user_data) +{ + struct wfp_static_filesystem * filesystem = user_data; + struct wfp_static_filesystem_entry * entry = wfp_impl_static_filesystem_get_entry_by_name(filesystem, parent, name); + + if (NULL != entry) + { + struct stat stat; + wfp_impl_static_filesystem_stat(entry, &stat); + wfp_respond_lookup(request, &stat); + } + else + { + wfp_respond_error(request, WF_BAD_NOENTRY); + } +} + + +static void wfp_impl_static_filesystem_getattr( + struct wfp_request * request, + ino_t inode, + void * user_data) +{ + struct wfp_static_filesystem * filesystem = user_data; + struct wfp_static_filesystem_entry * entry = wfp_impl_static_filesystem_get_entry(filesystem, inode); + + if (NULL != entry) + { + struct stat stat; + wfp_impl_static_filesystem_stat(entry, &stat); + wfp_respond_getattr(request, &stat); + } + else + { + wfp_respond_error(request, WF_BAD_NOENTRY); + } +} + +static void wfp_impl_static_filesystem_readdir( + struct wfp_request * request, + ino_t directory, + void * user_data) +{ + struct wfp_static_filesystem * filesystem = user_data; + struct wfp_static_filesystem_entry * dir = wfp_impl_static_filesystem_get_entry(filesystem, directory); + + if ((NULL != dir) && (!dir->is_file)) + { + struct wfp_dirbuffer * buffer = wfp_dirbuffer_create(); + wfp_dirbuffer_add(buffer, ".", dir->inode); + wfp_dirbuffer_add(buffer, "..", dir->inode); + + for(size_t i = 0; i < filesystem->size; i++) + { + struct wfp_static_filesystem_entry const * entry = &filesystem->entries[i]; + if (directory == entry->parent) + { + wfp_dirbuffer_add(buffer, entry->name, entry->inode); + } + } + + wfp_respond_readdir(request, buffer); + wfp_dirbuffer_dispose(buffer); + } + else + { + wfp_respond_error(request, WF_BAD_NOENTRY); + } +} + +static void wfp_impl_static_filesystem_open( + struct wfp_request * request, + ino_t inode, + int flags, + void * user_data) +{ + struct wfp_static_filesystem * filesystem = user_data; + struct wfp_static_filesystem_entry * entry = wfp_impl_static_filesystem_get_entry(filesystem, inode); + + if ((NULL != entry) && (entry->is_file)) + { + if (O_RDONLY == (flags & O_ACCMODE)) + { + wfp_respond_open(request, 0U); + } + else + { + wfp_respond_error(request, WF_BAD_ACCESS_DENIED); + } + } + else + { + wfp_respond_error(request, WF_BAD_NOENTRY); + } +} + +static void wfp_impl_static_filesystem_read( + struct wfp_request * request, + ino_t inode, + uint32_t WF_UNUSED_PARAM(handle), + size_t offset, + size_t length, + void * user_data) +{ + struct wfp_static_filesystem * filesystem = user_data; + struct wfp_static_filesystem_entry * entry = wfp_impl_static_filesystem_get_entry(filesystem, inode); + + if ((NULL != entry) && (entry->is_file)) + { + char buffer[WFP_STATIC_FILESYSTEM_MAX_READ_SIZE]; + size_t max_size = (length < WFP_STATIC_FILESYSTEM_MAX_READ_SIZE) ? length : WFP_STATIC_FILESYSTEM_MAX_READ_SIZE; + + size_t count = entry->read(offset, buffer, max_size, entry->user_data); + wfp_respond_read(request, buffer, count); + } + else + { + wfp_respond_error(request, WF_BAD_NOENTRY); + } +} + + +struct wfp_static_filesystem * +wfp_impl_static_filesystem_create( + struct wfp_client_config * config) +{ + (void) config; + + struct wfp_static_filesystem * filesystem = malloc(sizeof(struct wfp_static_filesystem)); + if (NULL != filesystem) + { + filesystem->entries = malloc(sizeof(struct wfp_static_filesystem_entry) * WFP_STATIC_FILESYSTEM_DEFAULT_CAPACITY); + filesystem->size = 0; + filesystem->capacity = WFP_STATIC_FILESYSTEM_DEFAULT_CAPACITY; + + wfp_impl_static_filesystem_add_dir(filesystem, 0, ""); + + wfp_client_config_set_userdata(config, filesystem); + wfp_client_config_set_onlookup(config, &wfp_impl_static_filesystem_lookup); + wfp_client_config_set_ongetattr(config, &wfp_impl_static_filesystem_getattr); + wfp_client_config_set_onreaddir(config, &wfp_impl_static_filesystem_readdir); + wfp_client_config_set_onopen(config, &wfp_impl_static_filesystem_open); + wfp_client_config_set_onread(config, &wfp_impl_static_filesystem_read); + } + + return filesystem; +} + +void +wfp_impl_static_filesystem_dispose( + struct wfp_static_filesystem * filesystem) +{ + for(size_t i = 0; i < filesystem->size; i++) + { + struct wfp_static_filesystem_entry * entry = &filesystem->entries[i]; + free(entry->name); + free(entry->content); + } + + free(filesystem->entries); + free(filesystem); +} + +void +wfp_impl_static_filesystem_add( + struct wfp_static_filesystem * filesystem, + char const * path, + int mode, + char const * content, + size_t length) +{ + struct wf_path * path_ = wf_path_create(path); + if (NULL != path_) + { + size_t parent = wfp_impl_static_filesystem_make_parent(filesystem, path_); + struct wfp_static_filesystem_entry * entry = wfp_impl_static_filesystem_add_entry(filesystem); + entry->parent = parent; + entry->is_file = true; + entry->name = strdup(wf_path_get_filename(path_)); + entry->mode = mode; + entry->size = length; + entry->get_info = &wfp_impl_static_filesystem_entry_get_info; + entry->read = &wfp_impl_static_filesystem_entry_read; + entry->user_data = entry; + + entry->content = malloc(length); + memcpy(entry->content, content, length); + + wf_path_dispose(path_); + } +} + +void +wfp_impl_static_filesystem_add_text( + struct wfp_static_filesystem * filesystem, + char const * path, + int mode, + char const * content) +{ + size_t length = strlen(content); + wfp_impl_static_filesystem_add(filesystem, path, mode, content, length); +} + +void +wfp_impl_static_filesystem_add_file( + struct wfp_static_filesystem * filesystem, + char const * path, + char const * filename) +{ + struct wf_path * path_ = wf_path_create(path); + if (NULL != path_) + { + size_t parent = wfp_impl_static_filesystem_make_parent(filesystem, path_); + struct wfp_static_filesystem_entry * entry = wfp_impl_static_filesystem_add_entry(filesystem); + entry->parent = parent; + entry->is_file = true; + entry->mode = 0; + entry->content = strdup(filename); + entry->size = 0; + entry->name = strdup(wf_path_get_filename(path_)); + entry->get_info = &wfp_impl_static_filesystem_file_get_info; + entry->read = &wfp_impl_static_filesystem_file_read; + entry->user_data = entry; + + wf_path_dispose(path_); + } +} + +void +wfp_impl_static_filesystem_add_generic( + struct wfp_static_filesystem * filesystem, + char const * path, + wfp_static_filesystem_read_fn * read, + wfp_static_filesystem_get_info_fn * get_info, + void * user_data) +{ + struct wf_path * path_ = wf_path_create(path); + if (NULL != path_) + { + size_t parent = wfp_impl_static_filesystem_make_parent(filesystem, path_); + struct wfp_static_filesystem_entry * entry = wfp_impl_static_filesystem_add_entry(filesystem); + entry->parent = parent; + entry->is_file = true; + entry->mode = 0; + entry->content = NULL; + entry->size = 0; + entry->name = strdup(wf_path_get_filename(path_)); + entry->get_info = get_info; + entry->read = read; + entry->user_data = user_data; + + wf_path_dispose(path_); + } +} diff --git a/lib/webfuse/provider/impl/static_filesystem.h b/lib/webfuse/provider/impl/static_filesystem.h new file mode 100644 index 0000000..a18835d --- /dev/null +++ b/lib/webfuse/provider/impl/static_filesystem.h @@ -0,0 +1,52 @@ +#ifndef WFP_IMPL_STATIC_FILESYSTEM_H +#define WFP_IMPL_STATIC_FILESYSTEM_H + +#include "webfuse/provider/static_filesystem.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern struct wfp_static_filesystem * +wfp_impl_static_filesystem_create( + struct wfp_client_config * config); + +extern void +wfp_impl_static_filesystem_dispose( + struct wfp_static_filesystem * filesystem); + +extern void +wfp_impl_static_filesystem_add( + struct wfp_static_filesystem * filesystem, + char const * path, + int mode, + char const * content, + size_t length); + +extern void +wfp_impl_static_filesystem_add_text( + struct wfp_static_filesystem * filesystem, + char const * path, + int mode, + char const * content); + +extern void +wfp_impl_static_filesystem_add_file( + struct wfp_static_filesystem * filesystem, + char const * path, + char const * filename); + +extern void +wfp_impl_static_filesystem_add_generic( + struct wfp_static_filesystem * filesystem, + char const * path, + wfp_static_filesystem_read_fn * read, + wfp_static_filesystem_get_info_fn * get_info, + void * user_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/json_matcher.hpp b/test/json_matcher.hpp new file mode 100644 index 0000000..7822344 --- /dev/null +++ b/test/json_matcher.hpp @@ -0,0 +1,34 @@ +#ifndef WF_JSON_MATCHER_HPP +#define FW_JSON_MATCHER_HPP + +#include +#include + +namespace webfuse_test +{ + +MATCHER_P(JsonMatcher, expected_str, "") +{ + std::cout << "--- JsonMatcher ---" << std::endl; + bool matches = false; + json_t * expected = json_loads(expected_str, 0, nullptr); + if (nullptr != expected) + { + matches = (1 == json_equal(expected, arg)); + if (!matches) + { + char * actual = json_dumps(arg, 0); + std::cout << actual << std::endl; + *result_listener << "where arg is " << actual; + free(actual); + } + + json_decref(expected); + } + + return true; //matches; +} + +} + +#endif diff --git a/test/mock_request.cc b/test/mock_request.cc new file mode 100644 index 0000000..efd8334 --- /dev/null +++ b/test/mock_request.cc @@ -0,0 +1,55 @@ +#include "mock_request.hpp" +#include + +namespace +{ + +extern "C" void +respond( + json_t * response, + void * user_data) +{ + webfuse_test::Request * request = reinterpret_cast(user_data); + + json_t * result = json_object_get(response, "result"); + json_t * error = json_object_get(response, "error"); + json_t * id_holder = json_object_get(response, "id"); + + int id = -1; + if (json_is_integer(id_holder)) + { + id = json_integer_value(id_holder); + } + + if (nullptr != result) + { + request->respond(result, id); + } + else + { + request->respond_error(error, id); + } +} + + +} + +namespace webfuse_test +{ + +struct wfp_request * +request_create( + Request * req, + int id) +{ + struct wfp_request * request = reinterpret_cast(malloc(sizeof(struct wfp_request))); + request->id = id; + request->user_data = reinterpret_cast(req); + request->respond = &respond; + + return request; +} + + + +} \ No newline at end of file diff --git a/test/mock_request.hpp b/test/mock_request.hpp new file mode 100644 index 0000000..51884f4 --- /dev/null +++ b/test/mock_request.hpp @@ -0,0 +1,122 @@ +#ifndef WF_MOCK_REQUEST_HPP +#define WF_MOCK_REQUEST_HPP + +#include +#include +#include +#include "webfuse/provider/impl/request.h" + + +namespace webfuse_test +{ + +class Request +{ +public: + virtual ~Request() { } + virtual void respond(json_t * result, int id) = 0; + virtual void respond_error(json_t * error, int id) = 0; +}; + +class MockRequest: public Request +{ +public: + MOCK_METHOD2(respond, void(json_t * result, int id)); + MOCK_METHOD2(respond_error, void(json_t * error, int id)); +}; + +extern struct wfp_request * +request_create( + Request * req, + int id); + +MATCHER_P3(GetAttrMatcher, inode, mode, file_type, "") +{ + json_t * inode_holder = json_object_get(arg, "inode"); + if ((!json_is_integer(inode_holder)) || (inode != json_integer_value(inode_holder))) + { + *result_listener << "missing inode"; + return false; + } + + json_t * mode_holder = json_object_get(arg, "mode"); + if ((!json_is_integer(mode_holder)) || (mode != json_integer_value(mode_holder))) + { + *result_listener << "missing mode"; + return false; + } + + json_t * type_holder = json_object_get(arg, "type"); + if ((!json_is_string(type_holder)) || (0 != strcmp(file_type, json_string_value(type_holder)))) + { + *result_listener << "missing type"; + return false; + } + + return true; +} + + +MATCHER_P(ReaddirMatcher, contained_elements , "") +{ + if (!json_is_array(arg)) + { + *result_listener << "result is not array"; + return false; + } + + { + size_t i; + json_t * value; + + json_array_foreach(arg, i, value) + { + json_t * inode = json_object_get(value, "inode"); + json_t * name = json_object_get(value, "name"); + + if(!json_is_integer(inode)) + { + *result_listener << "invalid result: missing inode"; + return false; + } + + if (!json_is_string(name)) + { + *result_listener << "invalid result: missing name"; + return false; + } + } + } + + for(size_t i = 0; NULL != contained_elements[i]; i++) + { + char const * element = contained_elements[i]; + bool found = false; + size_t j; + json_t * value; + + json_array_foreach(arg, j, value) + { + json_t * name = json_object_get(value, "name"); + + found = (0 == strcmp(element, json_string_value(name))); + if (found) + { + break; + } + } + + if (!found) + { + *result_listener << "missing required directory element: " << element; + return false; + } + } + + return true; +} + +} + + +#endif diff --git a/test/test_path.cc b/test/test_path.cc new file mode 100644 index 0000000..93783ec --- /dev/null +++ b/test/test_path.cc @@ -0,0 +1,58 @@ +#include +#include "webfuse/core/path.h" + +TEST(wf_path, empty) +{ + struct wf_path * path = wf_path_create(""); + ASSERT_EQ(0, wf_path_element_count(path)); + ASSERT_EQ(nullptr, wf_path_get_element(path, 0)); + + wf_path_dispose(path); +} + +TEST(wf_path, relative_file) +{ + struct wf_path * path = wf_path_create("some.file"); + ASSERT_EQ(1, wf_path_element_count(path)); + ASSERT_STREQ("some.file", wf_path_get_element(path, 0)); + + wf_path_dispose(path); +} + +TEST(wf_path, absolute_file) +{ + struct wf_path * path = wf_path_create("/absolute.file"); + ASSERT_EQ(1, wf_path_element_count(path)); + ASSERT_STREQ("absolute.file", wf_path_get_element(path, 0)); + + wf_path_dispose(path); +} + +TEST(wf_path, nested_path) +{ + struct wf_path * path = wf_path_create("/a/nested/path"); + ASSERT_EQ(3, wf_path_element_count(path)); + ASSERT_STREQ("a", wf_path_get_element(path, 0)); + ASSERT_STREQ("nested", wf_path_get_element(path, 1)); + ASSERT_STREQ("path", wf_path_get_element(path, 2)); + + wf_path_dispose(path); +} + +TEST(wf_path, deep_nested_path) +{ + struct wf_path * path = wf_path_create("/this/is/a/very/deep/nested/path/to/some/file"); + ASSERT_EQ(10, wf_path_element_count(path)); + ASSERT_STREQ("this", wf_path_get_element(path, 0)); + ASSERT_STREQ("is", wf_path_get_element(path, 1)); + ASSERT_STREQ("a", wf_path_get_element(path, 2)); + ASSERT_STREQ("very", wf_path_get_element(path, 3)); + ASSERT_STREQ("deep", wf_path_get_element(path, 4)); + ASSERT_STREQ("nested", wf_path_get_element(path, 5)); + ASSERT_STREQ("path", wf_path_get_element(path, 6)); + ASSERT_STREQ("to", wf_path_get_element(path, 7)); + ASSERT_STREQ("some", wf_path_get_element(path, 8)); + ASSERT_STREQ("file", wf_path_get_element(path, 9)); + + wf_path_dispose(path); +} \ No newline at end of file diff --git a/test/test_static_filesystem.cc b/test/test_static_filesystem.cc new file mode 100644 index 0000000..e7db28e --- /dev/null +++ b/test/test_static_filesystem.cc @@ -0,0 +1,61 @@ +#include + +#include "webfuse/provider/impl/static_filesystem.h" +#include "webfuse/provider/client_config.h" +#include "webfuse/provider/impl/client_config.h" + +#include "mock_request.hpp" + +using webfuse_test::request_create; +using webfuse_test::MockRequest; +using webfuse_test::GetAttrMatcher; +using webfuse_test::ReaddirMatcher; +using testing::_; + +TEST(wfp_static_filesystem, has_root_dir) +{ + struct wfp_client_config * config = wfp_client_config_create(); + struct wfp_static_filesystem * filesystem = wfp_impl_static_filesystem_create(config); + + MockRequest mock; + struct wfp_request * request = request_create(&mock, 42); + EXPECT_CALL(mock, respond(GetAttrMatcher(1, 0555, "dir"), 42)).Times(1); + + config->provider.getattr(request, 1, config->user_data); + + wfp_impl_static_filesystem_dispose(filesystem); + wfp_client_config_dispose(config); +} + +TEST(wfp_static_filesystem, contains_default_dirs) +{ + struct wfp_client_config * config = wfp_client_config_create(); + struct wfp_static_filesystem * filesystem = wfp_impl_static_filesystem_create(config); + + MockRequest mock; + struct wfp_request * request = request_create(&mock, 23); + char const * default_dirs[] = {".", "..", nullptr}; + EXPECT_CALL(mock, respond(ReaddirMatcher(default_dirs), 23)).Times(1); + + config->provider.readdir(request, 1, config->user_data); + + wfp_impl_static_filesystem_dispose(filesystem); + wfp_client_config_dispose(config); +} + +TEST(wfp_static_filesystem, add_text) +{ + struct wfp_client_config * config = wfp_client_config_create(); + struct wfp_static_filesystem * filesystem = wfp_impl_static_filesystem_create(config); + wfp_impl_static_filesystem_add_text(filesystem, "text.file", 666, "some text"); + + MockRequest mock; + struct wfp_request * request = request_create(&mock, 23); + char const * contained_elements[] = {"text.file", nullptr}; + EXPECT_CALL(mock, respond(ReaddirMatcher(contained_elements), 23)).Times(1); + + config->provider.readdir(request, 1, config->user_data); + + wfp_impl_static_filesystem_dispose(filesystem); + wfp_client_config_dispose(config); +}