From e00050073549038c6a50885a7581d19f25c89ac2 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sat, 4 Apr 2020 15:41:33 +0200 Subject: [PATCH] added unit tests for read operation (adapter); did some hardening --- cmake/unit_tests.cmake | 2 + lib/webfuse/adapter/impl/filesystem.c | 1 + lib/webfuse/adapter/impl/operation/read.c | 30 ++- lib/webfuse/adapter/impl/operation/read.h | 36 +++ lib/webfuse/adapter/impl/operations.h | 5 - test/webfuse/mocks/mock_fuse.cc | 1 + test/webfuse/mocks/mock_fuse.hpp | 1 + .../tests/adapter/operation/test_read.cc | 237 ++++++++++++++++++ 8 files changed, 303 insertions(+), 10 deletions(-) create mode 100644 lib/webfuse/adapter/impl/operation/read.h create mode 100644 test/webfuse/tests/adapter/operation/test_read.cc diff --git a/cmake/unit_tests.cmake b/cmake/unit_tests.cmake index 1ba7e5a..2b2d445 100644 --- a/cmake/unit_tests.cmake +++ b/cmake/unit_tests.cmake @@ -50,6 +50,7 @@ add_executable(alltests test/webfuse/tests/adapter/test_fuse_req.cc test/webfuse/tests/adapter/operation/test_open.cc test/webfuse/tests/adapter/operation/test_close.cc + test/webfuse/tests/adapter/operation/test_read.cc test/webfuse/tests/provider/test_url.cc test/webfuse/tests/provider/test_client_protocol.cc test/webfuse/tests/provider/operation/test_close.cc @@ -78,6 +79,7 @@ target_link_libraries(alltests PUBLIC -Wl,--wrap=fuse_req_userdata -Wl,--wrap=fuse_reply_open -Wl,--wrap=fuse_reply_err + -Wl,--wrap=fuse_reply_buf webfuse-adapter-static webfuse-provider-static diff --git a/lib/webfuse/adapter/impl/filesystem.c b/lib/webfuse/adapter/impl/filesystem.c index da9c87b..66ee76c 100644 --- a/lib/webfuse/adapter/impl/filesystem.c +++ b/lib/webfuse/adapter/impl/filesystem.c @@ -2,6 +2,7 @@ #include "webfuse/adapter/impl/operations.h" #include "webfuse/adapter/impl/operation/open.h" #include "webfuse/adapter/impl/operation/close.h" +#include "webfuse/adapter/impl/operation/read.h" #include "webfuse/adapter/impl/session.h" #include "webfuse/adapter/impl/mountpoint.h" diff --git a/lib/webfuse/adapter/impl/operation/read.c b/lib/webfuse/adapter/impl/operation/read.c index 7896b30..59c3180 100644 --- a/lib/webfuse/adapter/impl/operation/read.c +++ b/lib/webfuse/adapter/impl/operation/read.c @@ -1,3 +1,4 @@ +#include "webfuse/adapter/impl/operation/read.h" #include "webfuse/adapter/impl/operations.h" #include @@ -11,8 +12,9 @@ #define WF_MAX_READ_LENGTH 4096 -static char * wf_impl_fill_buffer( +char * wf_impl_fill_buffer( char const * data, + size_t data_size, char const * format, size_t count, wf_status * status) @@ -24,11 +26,22 @@ static char * wf_impl_fill_buffer( { if (0 == strcmp("identity", format)) { - memcpy(buffer, data, count); + if (count == data_size) + { + memcpy(buffer, data, count); + } + else + { + *status = WF_BAD; + } } else if (0 == strcmp("base64", format)) { - wf_base64_decode(data, strlen(data), (uint8_t *) buffer, count); + size_t result = wf_base64_decode(data, data_size, (uint8_t *) buffer, count); + if (result != count) + { + *status = WF_BAD; + } } else { @@ -36,10 +49,16 @@ static char * wf_impl_fill_buffer( } } + if (WF_GOOD != *status) + { + free(buffer); + buffer = NULL; + } + return buffer; } -static void wf_impl_operation_read_finished( +void wf_impl_operation_read_finished( void * user_data, json_t const * result, json_t const * error) @@ -60,10 +79,11 @@ static void wf_impl_operation_read_finished( json_is_integer(count_holder)) { char const * const data = json_string_value(data_holder); + size_t const data_size = json_string_length(data_holder); char const * const format = json_string_value(format_holder); length = (size_t) json_integer_value(count_holder); - buffer = wf_impl_fill_buffer(data, format, length, &status); + buffer = wf_impl_fill_buffer(data, data_size, format, length, &status); } else { diff --git a/lib/webfuse/adapter/impl/operation/read.h b/lib/webfuse/adapter/impl/operation/read.h new file mode 100644 index 0000000..89228b0 --- /dev/null +++ b/lib/webfuse/adapter/impl/operation/read.h @@ -0,0 +1,36 @@ +#ifndef WF_ADAPTER_IMPL_OPERATION_READ_H +#define WF_ADAPTER_IMPL_OPERATION_READ_H + +#include "webfuse/adapter/impl/fuse_wrapper.h" +#include "webfuse/core/status.h" + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void wf_impl_operation_read( + fuse_req_t request, + fuse_ino_t ino, size_t size, off_t off, + struct fuse_file_info *fi); + +extern char * wf_impl_fill_buffer( + char const * data, + size_t data_size, + char const * format, + size_t count, + wf_status * status); + +extern void wf_impl_operation_read_finished( + void * user_data, + json_t const * result, + json_t const * error); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/adapter/impl/operations.h b/lib/webfuse/adapter/impl/operations.h index 01808bc..6217de7 100644 --- a/lib/webfuse/adapter/impl/operations.h +++ b/lib/webfuse/adapter/impl/operations.h @@ -34,11 +34,6 @@ extern void wf_impl_operation_readdir ( off_t offset, struct fuse_file_info *file_info); -extern void wf_impl_operation_read( - fuse_req_t request, - fuse_ino_t ino, size_t size, off_t off, - struct fuse_file_info *fi); - extern struct wf_jsonrpc_proxy * wf_impl_operations_context_get_proxy( struct wf_impl_operations_context * context); diff --git a/test/webfuse/mocks/mock_fuse.cc b/test/webfuse/mocks/mock_fuse.cc index fa2f981..1cf6aa1 100644 --- a/test/webfuse/mocks/mock_fuse.cc +++ b/test/webfuse/mocks/mock_fuse.cc @@ -8,6 +8,7 @@ static webfuse_test::FuseMock * webfuse_test_FuseMock = nullptr; WF_WRAP_FUNC1(webfuse_test_FuseMock, void*, fuse_req_userdata, fuse_req_t); WF_WRAP_FUNC2(webfuse_test_FuseMock, int, fuse_reply_open, fuse_req_t, const struct fuse_file_info *); WF_WRAP_FUNC2(webfuse_test_FuseMock, int, fuse_reply_err, fuse_req_t, int); +WF_WRAP_FUNC3(webfuse_test_FuseMock, int, fuse_reply_buf, fuse_req_t, const char *, size_t); } diff --git a/test/webfuse/mocks/mock_fuse.hpp b/test/webfuse/mocks/mock_fuse.hpp index 07bcb60..1b81e71 100644 --- a/test/webfuse/mocks/mock_fuse.hpp +++ b/test/webfuse/mocks/mock_fuse.hpp @@ -16,6 +16,7 @@ public: MOCK_METHOD1(fuse_req_userdata, void *(fuse_req_t req)); MOCK_METHOD2(fuse_reply_open, int (fuse_req_t req, const struct fuse_file_info *fi)); MOCK_METHOD2(fuse_reply_err, int (fuse_req_t req, int err)); + MOCK_METHOD3(fuse_reply_buf, int (fuse_req_t req, const char *buf, size_t size)); }; } diff --git a/test/webfuse/tests/adapter/operation/test_read.cc b/test/webfuse/tests/adapter/operation/test_read.cc new file mode 100644 index 0000000..85fcf34 --- /dev/null +++ b/test/webfuse/tests/adapter/operation/test_read.cc @@ -0,0 +1,237 @@ +#include "webfuse/adapter/impl/operation/read.h" +#include "webfuse/adapter/impl/operations.h" + +#include "webfuse/mocks/mock_fuse.hpp" +#include "webfuse/mocks/mock_operations_context.hpp" +#include "webfuse/mocks/mock_jsonrpc_proxy.hpp" + +#include + +using webfuse_test::MockJsonRpcProxy; +using webfuse_test::MockOperationsContext; +using webfuse_test::FuseMock; +using testing::_; +using testing::Return; +using testing::StrEq; + +TEST(wf_impl_operation_read, invoke_proxy) +{ + MockJsonRpcProxy proxy; + EXPECT_CALL(proxy, wf_jsonrpc_proxy_vinvoke(_,_,_,StrEq("read"),StrEq("siiii"))).Times(1); + + MockOperationsContext context; + EXPECT_CALL(context, wf_impl_operations_context_get_proxy(_)).Times(1) + .WillOnce(Return(reinterpret_cast(&proxy))); + + wf_impl_operations_context op_context; + op_context.name = nullptr; + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(&op_context)); + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + size_t size = 42; + off_t offset = 0; + fuse_file_info file_info; + file_info.fh = 1; + wf_impl_operation_read(request, inode, size, offset, &file_info); +} + +TEST(wf_impl_operation_read, invoke_proxy_limit_size) +{ + MockJsonRpcProxy proxy; + EXPECT_CALL(proxy, wf_jsonrpc_proxy_vinvoke(_,_,_,StrEq("read"),StrEq("siiii"))).Times(1); + + MockOperationsContext context; + EXPECT_CALL(context, wf_impl_operations_context_get_proxy(_)).Times(1) + .WillOnce(Return(reinterpret_cast(&proxy))); + + wf_impl_operations_context op_context; + op_context.name = nullptr; + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(&op_context)); + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + size_t size = 100 * 1024 * 1024; + off_t offset = 0; + fuse_file_info file_info; + file_info.fh = 1; + wf_impl_operation_read(request, inode, size, offset, &file_info); +} + +TEST(wf_impl_operation_read, fail_rpc_null) +{ + MockOperationsContext context; + EXPECT_CALL(context, wf_impl_operations_context_get_proxy(_)).Times(1) + .WillOnce(Return(nullptr)); + + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(nullptr)); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + size_t size = 42; + off_t offset = 0; + fuse_file_info * file_info = nullptr; + wf_impl_operation_read(request, inode, size, offset, file_info); +} + +TEST(wf_impl_operation_read, fill_buffer_identity) +{ + wf_status status; + char * buffer = wf_impl_fill_buffer("brummni", 8, "identity", 8, &status); + ASSERT_EQ(WF_GOOD, status); + ASSERT_STREQ("brummni", buffer); + free(buffer); +} + +TEST(wf_impl_operation_read, fill_buffer_identity_fail_inconsistent_size) +{ + wf_status status; + char * buffer = wf_impl_fill_buffer("brummni", 8, "identity", 7, &status); + ASSERT_NE(WF_GOOD, status); + ASSERT_EQ(nullptr, buffer); +} + +TEST(wf_impl_operation_read, fill_buffer_base64) +{ + wf_status status; + char * buffer = wf_impl_fill_buffer("YnJ1bW1uaQ==", 12, "base64", 7, &status); + + ASSERT_EQ(WF_GOOD, status); + ASSERT_EQ(0, strncmp("brummni", buffer, 7)); + free(buffer); +} + +TEST(wf_impl_operation_read, fill_buffer_base64_fail_invalid_data) +{ + wf_status status; + char * buffer = wf_impl_fill_buffer("YnJ1bW1uaQ=A", 12, "base64", 8, &status); + ASSERT_NE(WF_GOOD, status); + ASSERT_EQ(nullptr, buffer); +} + +TEST(wf_impl_operation_read, fill_buffer_empty) +{ + wf_status status; + char * buffer = wf_impl_fill_buffer(nullptr, 0, "identity", 0, &status); + + ASSERT_EQ(WF_GOOD, status); + free(buffer); +} + +TEST(wf_impl_operation_read, fill_buffer_fail_invalid_format) +{ + wf_status status; + char * buffer = wf_impl_fill_buffer("some data", 9, "unknown", 9, &status); + ASSERT_NE(WF_GOOD, status); + ASSERT_EQ(nullptr, buffer); +} + +TEST(wf_impl_operation_read, finished) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,7)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "data", json_string("brummni")); + json_object_set_new(result, "format", json_string("identity")); + json_object_set_new(result, "count", json_integer(7)); + wf_impl_operation_read_finished(nullptr, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_read, finished_fail_no_data) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, _)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "format", json_string("identity")); + json_object_set_new(result, "count", json_integer(7)); + wf_impl_operation_read_finished(nullptr, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_read, finished_fail_invalid_data_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, _)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "data", json_integer(42)); + json_object_set_new(result, "format", json_string("identity")); + json_object_set_new(result, "count", json_integer(7)); + wf_impl_operation_read_finished(nullptr, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_read, finished_fail_no_format) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, _)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "data", json_string("brummni")); + json_object_set_new(result, "count", json_integer(7)); + wf_impl_operation_read_finished(nullptr, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_read, finished_fail_invalid_format_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, _)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "data", json_string("brummni")); + json_object_set_new(result, "format", json_integer(42)); + json_object_set_new(result, "count", json_integer(7)); + wf_impl_operation_read_finished(nullptr, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_read, finished_fail_no_count) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, _)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "data", json_string("brummni")); + json_object_set_new(result, "format", json_string("identity")); + wf_impl_operation_read_finished(nullptr, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_read, finished_fail_invalid_count_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, _)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "data", json_string("brummni")); + json_object_set_new(result, "format", json_string("identity")); + json_object_set_new(result, "count", json_string("7")); + wf_impl_operation_read_finished(nullptr, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_read, finished_fail_error) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, _)).Times(1).WillOnce(Return(0)); + + json_t * error = json_object(); + json_object_set_new(error, "code", json_integer(WF_BAD)); + wf_impl_operation_read_finished(nullptr, nullptr, error); + json_decref(error); +}