implemented some provider operations

pull/105/head
Falk Werner 1 year ago
parent 4e1c9e144c
commit a86061356a

@ -11,6 +11,8 @@ add_library(webfuse_static STATIC
src/webfuse/webfuse.cpp
src/webfuse/provider.cpp
src/webfuse/fuse.cpp
src/webfuse/request_type.cpp
src/webfuse/response_type.cpp
src/webfuse/filesystem.cpp
src/webfuse/filesystem/status.cpp
src/webfuse/filesystem/accessmode.cpp
@ -51,6 +53,8 @@ if(NOT(WITHOUT_TEST))
test-src/webfuse/test/thread.cpp
test-src/webfuse/test/tempdir.cpp
test-src/webfuse/test_app.cpp
test-src/webfuse/test_request_type.cpp
test-src/webfuse/test_response_type.cpp
test-src/webfuse/filesystem/test_status.cpp
test-src/webfuse/filesystem/test_accessmode.cpp
test-src/webfuse/filesystem/test_openflags.cpp

@ -1,4 +1,9 @@
#include "webfuse/provider.hpp"
#include <unistd.h>
#include <sys/stat.h>
#include <dirent.h>
#include <csignal>
#include <iostream>
@ -29,12 +34,20 @@ public:
int access(std::string const & path, int mode) override
{
return -ENOENT;
auto const full_path = get_full_path(path);
std::cout << "access: " << full_path << std::endl;
auto const result = ::access(full_path.c_str(), mode);
return (result == 0) ? 0 : -errno;
}
int getattr(std::string const & path, struct stat * attr) override
{
return -ENOENT;
auto const full_path = get_full_path(path);
std::cout << "getattr: " << full_path << std::endl;
auto const result = lstat(full_path.c_str(), attr);
return (result == 0) ? 0 : -errno;
}
int readlink(std::string const & path, std::string & out) override
@ -124,7 +137,27 @@ public:
int readdir(std::string const & path, std::vector<std::string> & entries, uint64_t handle) override
{
return -ENOENT;
auto const full_path = get_full_path(path);
std::cout << "readdir: " << full_path << std::endl;
int result = 0;
DIR * directory = opendir(full_path.c_str());
if (NULL != directory)
{
dirent * entry = ::readdir(directory);
while (entry != nullptr)
{
entries.push_back(std::string(entry->d_name));
entry = ::readdir(directory);
}
closedir(directory);
}
else
{
result = -errno;
}
return result;
}
int rmdir(std::string const & path) override
@ -139,6 +172,11 @@ public:
private:
std::string get_full_path(std::string const & path)
{
return base_path_ + path;
}
std::string base_path_;
};
@ -153,6 +191,12 @@ int main(int argc, char* argv[])
filesystem fs(".");
webfuse::provider provider(fs);
provider.set_connection_listener([](bool connected) {
if (!connected)
{
shutdown_requested = true;
}
});
provider.connect("ws://localhost:8080/");
while (!shutdown_requested)
{

@ -1,6 +1,11 @@
#include "webfuse/provider.hpp"
#include "webfuse/ws/client.hpp"
#include <unistd.h>
#include <sys/stat.h>
#include <iostream>
namespace webfuse
{
@ -29,24 +34,73 @@ public:
client.service();
}
void set_connection_listener(std::function<void(bool)> listener)
{
client.set_connection_listener(listener);
}
messagewriter on_message(messagereader & reader)
{
auto message_id = reader.read_u32();
auto request_type = reader.read_u8();
auto const message_id = reader.read_u32();
auto const req_type = get_request_type(reader.read_u8());
auto const resp_type = get_response_type(req_type);
messagewriter writer(response_type::unknown);
messagewriter writer(resp_type);
writer.set_id(message_id);
switch (request_type)
switch (req_type)
{
case request_type::access:
fs_access(reader, writer);
break;
case request_type::getattr:
fs_getattr(reader, writer);
break;
case request_type::readdir:
fs_readdir(reader, writer);
default:
std::cout << "unknown request: " << ((int) req_type) << std::endl;
break;
}
return std::move(writer);
}
private:
void fs_access(messagereader & reader, messagewriter & writer)
{
auto const path = reader.read_str();
auto const mode = reader.read_access_mode();
auto const result = fs_.access(path, mode);
writer.write_i32(result);
}
void fs_getattr(messagereader & reader, messagewriter & writer)
{
auto const path = reader.read_str();
struct stat buffer;
auto const result = fs_.getattr(path, &buffer);
writer.write_i32(result);
if (0 == result)
{
writer.write_attr(&buffer);
}
}
void fs_readdir(messagereader & reader, messagewriter & writer)
{
auto const path = reader.read_str();
std::vector<std::string> entries;
auto const result = fs_.readdir(path, entries, static_cast<uint64_t>(-1));
writer.write_i32(result);
if (0 == result)
{
writer.write_strings(entries);
}
}
filesystem_i & fs_;
ws_client client;
};
@ -90,5 +144,9 @@ void provider::service()
d->service();
}
void provider::set_connection_listener(std::function<void(bool)> listener)
{
d->set_connection_listener(listener);
}
}

@ -2,6 +2,7 @@
#define WEBFUSE_PROVIDER_I_HPP
#include "webfuse/filesystem/filesystem_i.hpp"
#include <functional>
#include <string>
namespace webfuse
@ -16,6 +17,7 @@ public:
~provider();
provider(provider && other);
provider& operator=(provider && other);
void set_connection_listener(std::function<void(bool)> listener);
void connect(std::string const & url);
void service();
private:

@ -0,0 +1,37 @@
#include "webfuse/request_type.hpp"
namespace webfuse
{
request_type get_request_type(uint8_t value)
{
switch (value)
{
case static_cast<uint8_t>(request_type::access): return request_type::access;
case static_cast<uint8_t>(request_type::getattr): return request_type::getattr;
case static_cast<uint8_t>(request_type::readlink): return request_type::readlink;
case static_cast<uint8_t>(request_type::symlink): return request_type::symlink;
case static_cast<uint8_t>(request_type::link): return request_type::link;
case static_cast<uint8_t>(request_type::rename): return request_type::rename;
case static_cast<uint8_t>(request_type::chmod): return request_type::chmod;
case static_cast<uint8_t>(request_type::chown): return request_type::chown;
case static_cast<uint8_t>(request_type::truncate): return request_type::truncate;
case static_cast<uint8_t>(request_type::fsync): return request_type::fsync;
case static_cast<uint8_t>(request_type::open): return request_type::open;
case static_cast<uint8_t>(request_type::mknod): return request_type::mknod;
case static_cast<uint8_t>(request_type::create): return request_type::create;
case static_cast<uint8_t>(request_type::release): return request_type::release;
case static_cast<uint8_t>(request_type::unlink): return request_type::unlink;
case static_cast<uint8_t>(request_type::read): return request_type::read;
case static_cast<uint8_t>(request_type::write): return request_type::write;
case static_cast<uint8_t>(request_type::mkdir): return request_type::mkdir;
case static_cast<uint8_t>(request_type::readdir): return request_type::readdir;
case static_cast<uint8_t>(request_type::rmdir): return request_type::rmdir;
case static_cast<uint8_t>(request_type::statfs): return request_type::statfs;
case static_cast<uint8_t>(request_type::utimens): return request_type::utimens;
default:
return request_type::unknown;
}
}
}

@ -33,6 +33,8 @@ enum class request_type: uint8_t
utimens = 0x16
};
request_type get_request_type(uint8_t value);
}
#endif

@ -0,0 +1,38 @@
#include "webfuse/response_type.hpp"
namespace webfuse
{
response_type get_response_type(request_type value)
{
switch (value)
{
case request_type::access: return response_type::access;
case request_type::getattr: return response_type::getattr;
case request_type::readlink: return response_type::readlink;
case request_type::symlink: return response_type::symlink;
case request_type::link: return response_type::link;
case request_type::rename: return response_type::rename;
case request_type::chmod: return response_type::chmod;
case request_type::chown: return response_type::chown;
case request_type::truncate: return response_type::truncate;
case request_type::fsync: return response_type::fsync;
case request_type::open: return response_type::open;
case request_type::mknod: return response_type::mknod;
case request_type::create: return response_type::create;
case request_type::release: return response_type::release;
case request_type::unlink: return response_type::unlink;
case request_type::read: return response_type::read;
case request_type::write: return response_type::write;
case request_type::mkdir: return response_type::mkdir;
case request_type::readdir: return response_type::readdir;
case request_type::rmdir: return response_type::rmdir;
case request_type::statfs: return response_type::statfs;
case request_type::utimens: return response_type::utimens;
default:
return response_type::unknown;
}
}
}

@ -1,6 +1,7 @@
#ifndef WEBFUSE_RESPONSE_TYPE
#define WEBFUSE_RESPONSE_TYPE
#include "request_type.hpp"
#include <cinttypes>
namespace webfuse
@ -33,6 +34,8 @@ enum class response_type: uint8_t
utimens = 0x96
};
response_type get_response_type(request_type value);
}
#endif

@ -3,42 +3,94 @@
#include <cstring>
#include <iostream>
#include <queue>
#include <string>
namespace
{
struct user_data
{
webfuse::ws_client_handler handler;
std::function<void(bool)> connection_listener;
struct lws * connection;
std::string current_message;
std::queue<webfuse::messagewriter> requests;
};
extern "C" int webfuse_client_callback(lws * wsi, lws_callback_reasons reason, void* user, void * in, size_t length)
{
int result = 0;
lws_protocols const * protocol = lws_get_protocol(wsi);
user_data * context = (nullptr != protocol) ? reinterpret_cast<user_data*>(protocol->user) : nullptr;
if (nullptr != protocol)
if (nullptr != context)
{
switch(reason)
{
case LWS_CALLBACK_CLIENT_ESTABLISHED:
std::cout << "established" << std::endl;
context->connection_listener(true);
break;
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
std::cout << "connect error" << std::endl;
break;
// fall-through
case LWS_CALLBACK_CLIENT_CLOSED:
std::cout << "closed" << std::endl;
context->connection = nullptr;
context->requests = std::move(std::queue<webfuse::messagewriter>());
context->current_message.clear();
context->connection_listener(false);
break;
case LWS_CALLBACK_CLIENT_RECEIVE:
std::cout << "receive" << std::endl;
{
std::cout << "receive" << std::endl;
auto * fragment = reinterpret_cast<char*>(in);
context->current_message.append(fragment, length);
if (lws_is_final_fragment(wsi))
{
try
{
webfuse::messagereader reader(context->current_message);
auto writer = context->handler(reader);
context->requests.emplace(std::move(writer));
lws_callback_on_writable(wsi);
}
catch(...)
{
// ToDo: log
std::cerr << "error: failed to create response" << std::endl;
}
}
}
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
// fall-through
case LWS_CALLBACK_CLIENT_WRITEABLE:
std::cout << "writable" << std::endl;
{
std::cout << "writable" << std::endl;
if (!context->requests.empty())
{
auto writer = std::move(context->requests.front());
context->requests.pop();
size_t size;
auto * data = writer.get_data(size);
lws_write(wsi, data, size, LWS_WRITE_BINARY);
}
if (!context->requests.empty())
{
lws_callback_on_writable(wsi);
}
}
break;
default:
break;
}
}
return result;
}
@ -55,13 +107,12 @@ class ws_client::detail
detail& operator=(detail &&) = delete;
public:
detail(ws_client_handler handler)
: handler_(handler)
{
memset(reinterpret_cast<void*>(protocols), 0, sizeof(lws_protocols) * 2);
protocols[0].callback = &webfuse_client_callback;
protocols[0].name = "webfuse2-client";
protocols[0].per_session_data_size = 0;
protocols[0].user = nullptr;
protocols[0].user = reinterpret_cast<void*>(&data);
memset(reinterpret_cast<void*>(&info), 0, sizeof(lws_context_creation_info));
info.port = CONTEXT_PORT_NO_LISTEN;
@ -69,6 +120,10 @@ public:
info.uid = -1;
info.gid = -1;
data.handler = handler;
data.connection_listener = [](bool){ };
data.connection = nullptr;
context = lws_create_context(&info);
}
@ -90,7 +145,7 @@ public:
info.ssl_connection = 0;
info.protocol = "webfuse2";
info.local_protocol_name = "webfuse2-client";
info.pwsi = &wsi;
info.pwsi = &data.connection;
lws_client_connect_via_info(&info);
}
@ -100,12 +155,21 @@ public:
lws_service(context, 0);
}
void interrupt()
{
lws_cancel_service(context);
}
void set_connection_listener(std::function<void(bool)> listener)
{
data.connection_listener = listener;
}
private:
ws_client_handler handler_;
lws_context_creation_info info;
lws_protocols protocols[2];
lws_context * context;
lws * wsi;
user_data data;
};
ws_client::ws_client(ws_client_handler handler)
@ -147,4 +211,14 @@ void ws_client::service()
d->service();
}
void ws_client::interrupt()
{
d->interrupt();
}
void ws_client::set_connection_listener(std::function<void(bool)> listener)
{
d->set_connection_listener(listener);
}
}

@ -21,8 +21,10 @@ public:
ws_client(ws_client && other);
ws_client& operator=(ws_client && other);
void set_connection_listener(std::function<void(bool)> listener);
void connect(std::string url);
void service();
void interrupt();
private:
class detail;
detail * d;

@ -1,6 +1,7 @@
#include "webfuse/ws/messagereader.hpp"
#include "webfuse/filesystem/status.hpp"
#include "webfuse/filesystem/filemode.hpp"
#include "webfuse/filesystem/accessmode.hpp"
#include <stdexcept>
@ -37,6 +38,14 @@ int messagereader::read_result()
return value.to_fusestatus();
}
int messagereader::read_access_mode()
{
auto const value = read_u8();
access_mode mode(static_cast<int8_t>(value));
return mode.to_int();
}
void messagereader::read_attr(struct stat * attr)
{
attr->st_ino = static_cast<ino_t>(read_u64());

@ -26,6 +26,7 @@ public:
int read_result();
void read_attr(struct stat * attr);
void read_statistics(struct statvfs * statistics);
int read_access_mode();
mode_t read_mode();
uint8_t read_u8();

@ -132,6 +132,21 @@ void messagewriter::write_strings(std::vector<std::string> const & list)
}
}
void messagewriter::write_attr(struct stat const * attr)
{
write_u64(static_cast<uint64_t>(attr->st_ino));
write_u64(static_cast<uint64_t>(attr->st_nlink));
write_mode(filemode::from_mode(attr->st_mode));
write_u32(static_cast<uint32_t>(attr->st_uid));
write_u32(static_cast<uint32_t>(attr->st_gid));
write_u64(static_cast<uint64_t>(attr->st_rdev));
write_u64(static_cast<uint64_t>(attr->st_size));
write_u64(static_cast<uint64_t>(attr->st_blocks));
write_time(attr->st_atim);
write_time(attr->st_mtim);
write_time(attr->st_ctim);
}
void messagewriter::write_access_mode(int value)
{
access_mode mode = access_mode::from_int(value);

@ -4,6 +4,8 @@
#include "webfuse/request_type.hpp"
#include "webfuse/response_type.hpp"
#include <sys/stat.h>
#include <cinttypes>
#include <string>
#include <vector>
@ -35,6 +37,7 @@ public:
void write_data(char const * buffer, size_t size);
void write_strings(std::vector<std::string> const & list);
void write_attr(struct stat const * attr);
void write_access_mode(int value);
void write_rename_flags(unsigned int value);
void write_mode(mode_t value);

@ -0,0 +1,37 @@
#include "webfuse/request_type.hpp"
#include <gtest/gtest.h>
using webfuse::request_type;
class request_type_test: public testing::TestWithParam<request_type> { };
TEST_P(request_type_test, conversion)
{
auto const expected = GetParam();
auto const actual = webfuse::get_request_type(static_cast<uint8_t>(expected));
ASSERT_EQ(expected, actual);
}
INSTANTIATE_TEST_CASE_P(request_type_values, request_type_test,
testing::Values(
request_type::access, request_type::getattr, request_type::readlink,
request_type::symlink, request_type::link, request_type::link,
request_type::rename, request_type::chmod, request_type::chown,
request_type::truncate, request_type::fsync, request_type::open,
request_type::mknod, request_type::create, request_type::release,
request_type::unlink, request_type::read, request_type::write,
request_type::mkdir, request_type::readdir, request_type::rmdir,
request_type::statfs, request_type::utimens, request_type::unknown)
);
TEST(request_type, unknown_values)
{
auto const expected = request_type::unknown;
ASSERT_EQ(expected, webfuse::get_request_type(0x20));
ASSERT_EQ(expected, webfuse::get_request_type(0x30));
ASSERT_EQ(expected, webfuse::get_request_type(0x80));
ASSERT_EQ(expected, webfuse::get_request_type(0x42));
ASSERT_EQ(expected, webfuse::get_request_type(0xff));
}

@ -0,0 +1,38 @@
#include "webfuse/response_type.hpp"
#include <gtest/gtest.h>
using webfuse::request_type;
class response_type_test: public testing::TestWithParam<request_type> { };
TEST_P(response_type_test, conversion)
{
auto const value = GetParam();
auto const actual = webfuse::get_response_type(value);
auto const expected = static_cast<webfuse::response_type>(static_cast<uint8_t>(value) | 0x80);
ASSERT_EQ(expected, actual);
}
INSTANTIATE_TEST_CASE_P(response_type_values, response_type_test,
testing::Values(
request_type::access, request_type::getattr, request_type::readlink,
request_type::symlink, request_type::link, request_type::link,
request_type::rename, request_type::chmod, request_type::chown,
request_type::truncate, request_type::fsync, request_type::open,
request_type::mknod, request_type::create, request_type::release,
request_type::unlink, request_type::read, request_type::write,
request_type::mkdir, request_type::readdir, request_type::rmdir,
request_type::statfs, request_type::utimens, request_type::unknown)
);
TEST(respones_type, unknown_values)
{
auto const expected = webfuse::response_type::unknown;
ASSERT_EQ(expected, webfuse::get_response_type(static_cast<request_type>(0x20)));
ASSERT_EQ(expected, webfuse::get_response_type(static_cast<request_type>(0x30)));
ASSERT_EQ(expected, webfuse::get_response_type(static_cast<request_type>(80)));
ASSERT_EQ(expected, webfuse::get_response_type(static_cast<request_type>(0x42)));
ASSERT_EQ(expected, webfuse::get_response_type(static_cast<request_type>(0xff)));
}
Loading…
Cancel
Save