1
0
mirror of https://github.com/falk-werner/webfuse synced 2024-10-27 20:34:10 +00:00

feat(webfuse): add multiclient support (#23)

* fixes verbosity option when set through command line

* adds support for build type and allows to run gdb in container

* adds missing toolchain headers to project

* renames container macros

* adds gdbserver

* fixes verbosity option when set through command line

* adds support for build type and allows to run gdb in container

* adds missing toolchain headers to project

* renames container macros

* adds gdbserver

* removes language settings, which contains alternating values

* adds wrapper script to launch gdbserver

* fix docker command in wrapper script

* fixes run in dind setup

* replaces docker's init through dump-init

* moves filesystem to session

* fixes verbosity option when set through command line

* adds support for build type and allows to run gdb in container

* renames container macros

* adds gdbserver

* fixes verbosity option when set through command line

* adds support for build type and allows to run gdb in container

* renames container macros

* adds gdbserver

* adds wrapper script to launch gdbserver

* fix docker command in wrapper script

* fixes run in dind setup

* replaces docker's init through dump-init

* moves filesystem to session

* adds container_of

* added dlist

* allows multiple clients to connect

* removes directory when session is closed

* adds dependecy to uuid-dev

* allow clients to register filesystems

* updates documentation

* moves mountpoint handling into filesystem: mountpoints are removed during session cleanup

* adds filesystem name/id to request parameters

* fixes security issue: add_filesystem did not check name

* removes default link, if it is broken

* recreates symlink "default", if filesystem is gone

* updates documentation

* fixes memory leak

* makes authentication work .. again

* updates provider to support changed protocol

* removes execute right of hello.txt

* fixes style issues

* fixes javascript style issues

* fixes flase positive from Flawfinder

* fixes some javascript style issues

* removes use of PATH_MAX

* removes use of GNU extensions in container_of implementation

* ignores findings of flawfinder

* replaces dlist by slist

* removes duplicate implementation of slist (message_queue)
This commit is contained in:
Falk Werner 2019-04-17 22:51:16 +02:00 committed by nosamad
parent 979ff1e689
commit 3a7c064af7
52 changed files with 997 additions and 337 deletions

View File

@ -11,6 +11,7 @@ find_package(PkgConfig REQUIRED)
pkg_check_modules(FUSE3 REQUIRED fuse3)
pkg_check_modules(LWS REQUIRED libwebsockets)
pkg_check_modules(JANSSON REQUIRED jansson)
pkg_check_modules(UUID REQUIRED uuid)
add_definitions(-D_FILE_OFFSET_BITS=64)
@ -26,6 +27,7 @@ set(EXTRA_INCLUDE_DIRS
${FUSE3_INCLUDE_DIRS}
${LWS_INCLUDE_DIRS}
${JANSSON_INCLUDE_DIRS}
${UUID_INCLUDE_DIRS}
)
set(EXTRA_LIBS
@ -33,6 +35,7 @@ set(EXTRA_LIBS
${FUSE3_LIBRARIES}
${LWS_LIBRARIES}
${JANSSON_LIBRARIES}
${UUID_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
)
@ -41,6 +44,7 @@ set(EXTRA_CFLAGS
${FUSE3_CFLAGS_OTHER}
${LWS_CFLAGS_OTHER}
${JANSSON_CFLAGS_OTHER}
${UUID_CFLAGS_OTHER}
"-pthread"
)
@ -48,9 +52,11 @@ set(EXTRA_CFLAGS
# libwebfuse-core
add_library(webfuse-core STATIC
lib/webfuse/core/slist.c
lib/webfuse/core/message.c
lib/webfuse/core/message_queue.c
lib/webfuse/core/status.c
lib/webfuse/core/string.c
)
set_target_properties(webfuse-core PROPERTIES OUTPUT_NAME webfuse-core)
@ -272,6 +278,7 @@ pkg_check_modules(GMOCK gmock)
add_executable(alltests
test/msleep.cc
test/mock_authenticator.cc
test/test_container_of.cc
test/test_response_parser.cc
test/test_server.cc
test/test_timepoint.cc
@ -280,6 +287,8 @@ add_executable(alltests
test/test_credentials.cc
test/test_authenticator.cc
test/test_authenticators.cc
test/test_string.cc
test/test_slist.cc
)
target_link_libraries(alltests PUBLIC webfuse-adapter-static webfuse-provider-static webfuse-core ${EXTRA_LIBS} ${GMOCK_LIBRARIES} ${GTEST_LIBRARIES})

View File

@ -38,16 +38,35 @@ webfuse solves this problem by using the [WebSocket](https://en.wikipedia.org/wi
With webfuse it is possible to implement remote filesystems based on websockets.
A reference implementation of such a daemon is provided within the examples. The picture above describes the workflow:
- The websocket filesystem daemon (*webfuse daemon*) mounts a filesystem on startup.
It starts the websocket server and waits for incoming connections.
- The websocket filesystem daemon (*webfuse daemon*) waits for incoming connections.
- A remote filesystem provider connects to webfuse daemon via websocket protocol.
The example includes such a provider implemented in HTML and JavaScript.
- A remote filesystem provider connects to webfuse daemon via websocket protocol and adds one or more filesystems.
*Note: the examples include such a provider implemented in HTML and JavaScript.*
- Whenever the user makes filesystem requests, such as *ls*, the request is redirected via webfuse daemon to the connected filesystem provider
Currently all requests are initiated by webfuse daemon and responded by filesystem provider. This may change in future, e.g. when authentication is supported.
### Filesystem represenation
![filesystem](doc/filesystem.png)
To handle multiple filesystems, that are registered by one or more providers, webfuse daemon maintains a directory structure as shown above.
- **mount_point** is the entry point of the drectory structure
- **fwupdate** is a name defined by the provider when filesystem was registered
*Note: the picture above shows two providers, where both registered a filesystem named "fwupdate"*
- **<uuid>** is the filesystem id choosen by webfuse daemon to distinguish different filesystems
- **default** is a symbolic link maintained by webfuse daemon to identify the default filesystem
This directoy structure allows to handle multiple filesystems registered by multiple providers.
It can be used as a kind of service registry, where each filesystem represents a service.
The named subdirectores distinguish differend service types. The symbolic link *default* can be used to identify the
default service and the listing of a named subdirectory can be used to list available services of a particular type.
## Similar Projects
### Davfs2
@ -141,7 +160,7 @@ Notfications are used to inform a receiver about something. Unlike requests, not
Retrieve information about a filesystem entry by name.
webfuse daemon: {"method": "lookup", "params": [<parent>, <name>], "id": <id>}
webfuse daemon: {"method": "lookup", "params": [<filesystem>, <parent>, <name>], "id": <id>}
fs provider: {"result": {
"inode": <inode>,
"mode" : <mode>,
@ -154,6 +173,7 @@ Retrieve information about a filesystem entry by name.
| Item | Data type | Description |
| ----------- | --------------- | ------------------------------------------- |
| filesystem | string | name of the filesystem |
| parent | integer | inode of parent directory (1 = root) |
| name | string | name of the filesystem object to look up |
| inode | integer | inode of the filesystem object |
@ -168,7 +188,7 @@ Retrieve information about a filesystem entry by name.
Get file attributes.
webfuse daemon: {"method": "getattr", "params": [<inode>], "id": <id>}
webfuse daemon: {"method": "getattr", "params": [<filesystem>, <inode>], "id": <id>}
fs provider: {"result": {
"mode" : <mode>,
"type" : <type>,
@ -180,6 +200,7 @@ Get file attributes.
| Item | Data type | Description |
| ----------- | --------------- | ------------------------------------------- |
| filesystem | string | name of the filesystem |
| inode | integer | inode of the filesystem object |
| mode | integer | unix file mode |
| type | "file" or "dir" | type of filesystem object |
@ -194,7 +215,7 @@ Read directory contents.
Result is an array of name-inode pairs for each entry. The generic entries
"." and ".." should also be provided.
webfuse daemon: {"method": "readdir", "params": [<dir_inode>], "id": <id>}
webfuse daemon: {"method": "readdir", "params": [<filesystem>, <dir_inode>], "id": <id>}
fs provider: {"result": [
{"name": <name>, "inode": <inode>},
...
@ -202,6 +223,7 @@ Result is an array of name-inode pairs for each entry. The generic entries
| Item | Data type | Description |
| ----------- | --------------- | ------------------------------ |
| filesystem | string | name of the filesystem |
| dir_inode | integer | inode of the directory to read |
| name | integer | name of the entry |
| inode | integer | inode of the entry |
@ -210,11 +232,12 @@ Result is an array of name-inode pairs for each entry. The generic entries
Open a file.
webfuse daemon: {"method": "readdir", "params": [<inode>, <flags>], "id": <id>}
webfuse daemon: {"method": "readdir", "params": [<filesystem>, <inode>, <flags>], "id": <id>}
fs provider: {"result": {"handle": <handle>}, "id": <id>}
| Item | Data type | Description |
| ----------- | ----------| ----------------------------- |
| filesystem | string | name of the filesystem |
| inode | integer | inode of the file |
| flags | integer | access mode flags (see below) |
| handle | integer | handle of the file |
@ -237,10 +260,11 @@ Open a file.
Informs filesystem provider, that a file is closed.
Since `close` is a notification, it cannot fail.
webfuse daemon: {"method": "close", "params": [<inode>, <handle>, <flags>], "id": <id>}
webfuse daemon: {"method": "close", "params": [<filesystem>, <inode>, <handle>, <flags>], "id": <id>}
| Item | Data type | Description |
| ----------- | ----------| ---------------------------- |
| filesystem | string | name of the filesystem |
| inode | integer | inode of the file |
| handle | integer | handle of the file |
| flags | integer | access mode flags (see open) |
@ -249,7 +273,7 @@ Since `close` is a notification, it cannot fail.
Read from an open file.
webfuse daemon: {"method": "close", "params": [<inode>, <handle>, <offset>, <length>], "id": <id>}
webfuse daemon: {"method": "close", "params": [<filesystem>, <inode>, <handle>, <offset>, <length>], "id": <id>}
fs provider: {"result": {
"data": <data>,
"format": <format>,
@ -258,6 +282,7 @@ Read from an open file.
| Item | Data type | Description |
| ----------- | ----------| ----------------------------- |
| filesystem | string | name of the filesystem |
| inode | integer | inode of the file |
| handle | integer | handle of the file |
| offset | integer | Offet to start read operation |
@ -275,10 +300,21 @@ Read from an open file.
### Requests (Provider -> Adapter)
#### add_filesystem
Adds a filesystem.
fs provider: {"method": "add_filesytem", "params": [<name>], "id": <id>}
webfuse daemon: {"result": {"id": <name>}, "id": <id>}
| Item | Data type | Description |
| ----------- | ----------| ------------------------------- |
| name | string | name and id of filesystem |
#### authtenticate
Authenticate the provider.
If authentication is enabled, a provider must be authenticated by the adapter before the adapter will send any messages.
If authentication is enabled, a provider must be authenticated by the adapter before filesystems can be added.
fs provider: {"method": "authenticate", "params": [<type>, <credentials>], "id": <id>}
webfuse daemon: {"result": {}, "id": <id>}

View File

@ -14,7 +14,8 @@ RUN set -x \
rsync \
gdb \
gdbserver \
valgrind
valgrind \
uuid-dev
COPY src /usr/local/src

View File

@ -17,7 +17,8 @@ RUN set -x \
pkg-config \
rsync \
gdb \
gdbserver
gdbserver \
uuid-dev
COPY src /usr/local/src

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -1,12 +1,10 @@
@startuml
participant "Filesystem Provider\n(e.g. Webbrowser)" as provider
participant "webfuse\ndaemon" as daemon
actor "user" as user
group directory listing fails without authentication
user -> daemon : ls
daemon -> daemon : is_authenticated
daemon -->x user : error: no entry
group add filesystem fails without authentication
provider -> daemon : add_filesystem
daemon -->x provider : error: access denied
end
@ -17,12 +15,10 @@ daemon -> daemon: check(credentials)
daemon --> provider: result
end
group directory listing succeeds after authentication
user -> daemon : ls
daemon -> daemon : is_authenticated
daemon -> provider : readdir
provider --> daemon : readdir_resp
daemon --> user : [., ..]
group add filesystem succeeds after authentication
provider -> daemon: add_filesystem
daemon -> daemon : fuse_mount
daemon -> provider: okay
end
@enduml

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -4,7 +4,6 @@ participant "webfuse\ndaemon" as daemon
actor "user" as user
group startup
daemon -> daemon : fuse_mount
daemon -> daemon : start ws
end
...
@ -16,9 +15,15 @@ provider -> daemon : connect
daemon -> daemon: check credentials
daemon --> provider: okay
end
provider -> daemon: add_filesystem
daemon -> daemon : fuse_mount
daemon -> provider: okay
end
...
group directory listing
user -> daemon : ls
daemon -> provider : readdir

BIN
doc/filesystem.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

16
doc/filesystem.uml Normal file
View File

@ -0,0 +1,16 @@
@startuml
salt
{
{T
+ mount_point
++ fwupdate
+++ default -> 7c029f81-6bdf-4d3c-82dc-26f748164012
+++ 7c029f81-6bdf-4d3c-82dc-26f748164012
++++ update.raucb
+++ f93de23b-4535-4a47-a287-a381b78a11b8
++++ update.raucb
}
}
@enduml

View File

@ -50,13 +50,14 @@ static bool authenticate(struct wf_credentials * creds, void * user_data)
char const * password = wf_credentials_get(creds, "password");
if ((NULL != username) && (NULL != password))
{
struct userdb * db = userdb_create("<pepper>");
struct userdb * db = userdb_create("");
result = userdb_load(db, args->passwd_path);
if (result)
{
result = userdb_check(db, username, password);
userdb_dispose(db);
}
userdb_dispose(db);
}
return result;

View File

@ -1,5 +1,6 @@
export class ConnectionView {
constructor(client) {
constructor(client, provider) {
this._provider = provider;
this._client = client;
this._client.onopen = () => { this._onConnectionOpened(); };
this._client.onclose = () => { this._onConnectionClosed(); };
@ -28,6 +29,14 @@ export class ConnectionView {
const authenticateBox = document.createElement("div");
this.element.appendChild(authenticateBox);
const authLabel = document.createElement("span");
authLabel.textContent = "use authentication:";
authenticateBox.appendChild(authLabel);
this.authenticateCheckbox = document.createElement("input");
this.authenticateCheckbox.type = "checkbox";
authenticateBox.appendChild(this.authenticateCheckbox);
const usernameLabel = document.createElement("span");
usernameLabel.textContent = "user:";
authenticateBox.appendChild(usernameLabel);
@ -45,12 +54,6 @@ export class ConnectionView {
this.passwordTextbox.type = "password";
this.passwordTextbox.value = "secret";
authenticateBox.appendChild(this.passwordTextbox);
this.authenticateButton = document.createElement("input");
this.authenticateButton.type = "button";
this.authenticateButton.value = "authenticate";
this.authenticateButton.addEventListener("click", () => { this._onAuthenticateButtonClicked(); });
authenticateBox.appendChild(this.authenticateButton);
}
_onConnectButtonClicked() {
@ -65,14 +68,21 @@ export class ConnectionView {
_onAuthenticateButtonClicked() {
if (this._client.isConnected()) {
const username = this.usernameTextbox.value;
const password = this.passwordTextbox.value;
this._client.authenticate("username", { username, password });
}
}
_onConnectionOpened() {
if (this.authenticateCheckbox.checked) {
const username = this.usernameTextbox.value;
const password = this.passwordTextbox.value;
const promise = this._client.authenticate("username", { username, password });
promise.then(() => { this._client.addProvider("test", this._provider); });
} else {
this._client.addProvider("test", this._provider);
}
this.connectButton.value = "disconnect";
}

View File

@ -17,8 +17,8 @@ function startup() {
"say_hello.sh": { inode: 3, mode: mode("0555"), type: "file", contents: "#!/bin/sh\necho hello\n"}
}
});
const client = new Client(provider);
const connectionView = new ConnectionView(client);
const client = new Client();
const connectionView = new ConnectionView(client, provider);
document.getElementById('connection').appendChild(connectionView.element);
}

View File

@ -4,7 +4,9 @@ export class Client {
static get _PROTOCOL() { return "fs"; }
constructor(provider) {
this._provider = provider;
this._provider = { };
this._pendingRequests = {};
this._id = 0;
this._ws = null;
this.onopen = () => { };
this.onclose = () => { };
@ -24,11 +26,27 @@ export class Client {
};
}
_invokeRequest(method, params) {
this._id += 1;
const id = this._id;
const request = {method, params, id};
return new Promise((resolve, reject) => {
this._pendingRequests[id] = {resolve, reject};
this._ws.send(JSON.stringify(request));
});
}
authenticate(type, credentials) {
return this._invokeRequest("authenticate", [type, credentials]);
}
addProvider(name, provider) {
this._provider[name] = provider;
const request = {
"method": "authenticate",
"params": [type, credentials],
"id": 42
"method": "add_filesystem",
"params": [name],
"id": 23
};
this._ws.send(JSON.stringify(request));
@ -45,27 +63,61 @@ export class Client {
return ((this._ws) && (this._ws.readyState === WebSocket.OPEN));
}
_isRequest(request) {
const method = request.method;
return (("string" === typeof(method)) && ("params" in request));
}
_isResponse(response) {
const id = response.id;
return (("number" === typeof(id)) && (("result" in response) || ("error" in response)));
}
_removePendingRequest(id) {
let result = null;
if (id in this._pendingRequests) {
result = this._pendingRequests[id];
Reflect.deleteProperty(this._pendingRequests, id);
}
return result;
}
_onmessage(message) {
try {
const request = JSON.parse(message.data);
const method = request.method;
const id = request.id;
const params = request.params;
const data = JSON.parse(message.data);
if ("string" !== typeof(method)) {
throw new Error("parse error: missing field: \"method\"");
if (this._isRequest(data)) {
const method = data.method;
const id = data.id;
const params = data.params;
if ("number" === typeof(id)) {
this._invoke(method, params, id);
}
else {
this._notify(method, params);
}
}
else if (this._isResponse(data)) {
const id = data.id;
const result = data.result;
const error = data.error;
const request = this._removePendingRequest(id);
if (request) {
if (result) {
request.resolve(result);
}
else {
request.reject(error);
}
}
}
if (!params) {
throw new Error("parse error: missing field: \"params\"");
}
if ("number" === typeof(request.id)) {
this._invoke(method, params, id);
}
else {
this._notify(method, params);
}
}
catch (ex) {
// swallow
@ -114,28 +166,48 @@ export class Client {
}
}
async _lookup([parent, name]) {
return this._provider.lookup(parent, name);
_getProvider(name) {
if (name in this._provider) {
return this._provider[name];
}
else {
throw new Error('Unknown provider');
}
}
async _getattr([inode]) {
return this._provider.getattr(inode);
async _lookup([providerName, parent, name]) {
const provider = this._getProvider(providerName);
return provider.lookup(parent, name);
}
async _readdir([inode]) {
return this._provider.readdir(inode);
async _getattr([providerName, inode]) {
const provider = this._getProvider(providerName);
return provider.getattr(inode);
}
async _open([inode, mode]) {
return this._provider.open(inode, mode);
async _readdir([providerName, inode]) {
const provider = this._getProvider(providerName);
return provider.readdir(inode);
}
_close([inode, handle, mode]) {
this._provider.close(inode, handle, mode);
async _open([providerName, inode, mode]) {
const provider = this._getProvider(providerName);
return provider.open(inode, mode);
}
async _read([inode, handle, offset, length]) {
const data = await this._provider.read(inode, handle, offset, length);
_close([providerName, inode, handle, mode]) {
const provider = this._getProvider(providerName);
provider.close(inode, handle, mode);
}
async _read([providerName, inode, handle, offset, length]) {
const provider = this._getProvider(providerName);
const data = await provider.read(inode, handle, offset, length);
if ("string" === typeof(data)) {
return {

View File

@ -340,7 +340,7 @@ int main(int argc, char* argv[])
.parent = 1,
.inode = 2,
.name = "hello.txt",
.mode = 0555,
.mode = 0444,
.type = FS_FILE,
.content="hello, world!",
.content_length = 13,

View File

@ -1,5 +1,16 @@
#include "webfuse/adapter/impl/filesystem.h"
#include "webfuse/adapter/impl/operations.h"
#include "webfuse/adapter/impl/session.h"
#include "webfuse/core/string.h"
#include <libwebsockets.h>
#include <uuid/uuid.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h>
#include <stddef.h>
@ -16,23 +27,118 @@ static struct fuse_lowlevel_ops const filesystem_operations =
.read = &wf_impl_operation_read
};
static char * wf_impl_filesystem_create_id(void)
{
uuid_t uuid;
uuid_generate(uuid);
char id[UUID_STR_LEN];
uuid_unparse(uuid, id);
bool wf_impl_filesystem_init(
struct wf_impl_filesystem * filesystem,
struct wf_impl_session_manager * session_manager,
char * mount_point)
return strdup(id);
}
static bool wf_impl_filesystem_is_link_broken(char const * path, char const * id)
{
bool result = false;
char buffer[UUID_STR_LEN];
ssize_t count = readlink(path, buffer, UUID_STR_LEN);
if ((0 < count) && (count < UUID_STR_LEN))
{
buffer[count] = '\0';
result = (0 == strcmp(buffer, id));
}
return result;
}
static bool wf_impl_filesystem_link_first_subdir(
char const * link_path,
char const * path)
{
bool result = false;
DIR * dir = opendir(path);
if (NULL != dir)
{
struct dirent * entry = readdir(dir);
while (NULL != entry)
{
if ((DT_DIR == entry->d_type) && ('.' != entry->d_name[0]))
{
symlink(entry->d_name, link_path);
result = true;
break;
}
entry = readdir(dir);
}
closedir(dir);
}
return result;
}
static void wf_impl_filesystem_cleanup(
struct wf_impl_filesystem * filesystem)
{
fuse_session_reset(filesystem->session);
fuse_session_unmount(filesystem->session);
fuse_session_destroy(filesystem->session);
filesystem->session = NULL;
free(filesystem->buffer.mem);
fuse_opt_free_args(&filesystem->args);
rmdir(filesystem->root_path);
if (wf_impl_filesystem_is_link_broken(filesystem->default_path, filesystem->id))
{
unlink(filesystem->default_path);
bool const success = wf_impl_filesystem_link_first_subdir(filesystem->default_path, filesystem->service_path);
if (!success)
{
rmdir(filesystem->service_path);
}
}
free(filesystem->user_data.name);
free(filesystem->id);
free(filesystem->root_path);
free(filesystem->default_path);
free(filesystem->service_path);
}
static bool wf_impl_filesystem_init(
struct wf_impl_filesystem * filesystem,
struct wf_impl_session * session,
char const * name)
{
bool result = false;
char * argv[] = {"", NULL};
filesystem->args.argc = 1;
filesystem->args.argv = argv;
filesystem->args.allocated = 0;
filesystem->user_data.session_manager = session_manager;
filesystem->user_data.session = session;
filesystem->user_data.timeout = 1.0;
filesystem->user_data.name = strdup(name);
memset(&filesystem->buffer, 0, sizeof(struct fuse_buf));
filesystem->service_path = wf_create_string("%s/%s", session->mount_point, name);
mkdir(filesystem->service_path, 0755);
filesystem->id = wf_impl_filesystem_create_id();
filesystem->root_path = wf_create_string("%s/%s/%s", session->mount_point, name, filesystem->id);
mkdir(filesystem->root_path, 0755);
filesystem->default_path = wf_create_string("%s/%s/default", session->mount_point, name);
symlink(filesystem->id, filesystem->default_path);
filesystem->session = fuse_session_new(
&filesystem->args,
&filesystem_operations,
@ -40,31 +146,50 @@ bool wf_impl_filesystem_init(
&filesystem->user_data);
if (NULL != filesystem->session)
{
result = (0 == fuse_session_mount(filesystem->session, mount_point));
result = (0 == fuse_session_mount(filesystem->session, filesystem->root_path));
}
if (result)
{
lws_sock_file_fd_type fd;
fd.filefd = fuse_session_fd(filesystem->session);
struct lws_protocols const * protocol = lws_get_protocol(session->wsi);
filesystem->wsi = lws_adopt_descriptor_vhost(lws_get_vhost(session->wsi), LWS_ADOPT_RAW_FILE_DESC, fd, protocol->name, session->wsi);
if (NULL == filesystem->wsi)
{
wf_impl_filesystem_cleanup(filesystem);
result = false;
}
}
return result;
}
void wf_impl_filesystem_cleanup(
struct wf_impl_filesystem * filesystem)
struct wf_impl_filesystem * wf_impl_filesystem_create(
struct wf_impl_session * session,
char const * name)
{
if (NULL != filesystem->session)
struct wf_impl_filesystem * filesystem = malloc(sizeof(struct wf_impl_filesystem));
if (NULL != filesystem)
{
fuse_session_reset(filesystem->session);
fuse_session_unmount(filesystem->session);
fuse_session_destroy(filesystem->session);
filesystem->session = NULL;
bool success = wf_impl_filesystem_init(filesystem, session, name);
if (!success)
{
free(filesystem);
filesystem = NULL;
}
}
free(filesystem->buffer.mem);
fuse_opt_free_args(&filesystem->args);
return filesystem;
}
int wf_impl_filesystem_get_fd(
void wf_impl_filesystem_dispose(
struct wf_impl_filesystem * filesystem)
{
return fuse_session_fd(filesystem->session);
wf_impl_filesystem_cleanup(filesystem);
free(filesystem);
}
void wf_impl_filesystem_process_request(

View File

@ -7,31 +7,36 @@
#include "webfuse/adapter/impl/fuse_wrapper.h"
#include "webfuse/adapter/impl/operations.h"
#include "webfuse/core/slist.h"
#ifdef __cplusplus
extern "C"
{
#endif
struct wf_impl_session_manager;
struct wf_impl_session;
struct lws;
struct wf_impl_filesystem
{
struct wf_slist_item item;
struct fuse_args args;
struct fuse_session * session;
struct fuse_buf buffer;
struct wf_impl_operations_context user_data;
struct lws * wsi;
char * name;
char * id;
char * service_path;
char * default_path;
char * root_path;
};
extern bool wf_impl_filesystem_init(
struct wf_impl_filesystem * filesystem,
struct wf_impl_session_manager * session_manager,
char * mount_point);
extern struct wf_impl_filesystem * wf_impl_filesystem_create(
struct wf_impl_session * session,
char const * name);
extern void wf_impl_filesystem_cleanup(
struct wf_impl_filesystem * filesystem);
extern int wf_impl_filesystem_get_fd(
extern void wf_impl_filesystem_dispose(
struct wf_impl_filesystem * filesystem);
extern void wf_impl_filesystem_process_request(

View File

@ -13,12 +13,12 @@ void wf_impl_operation_close(
struct fuse_file_info * file_info)
{
struct wf_impl_operations_context * user_data = fuse_req_userdata(request);
struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, inode);
struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data);
if (NULL != rpc)
{
int handle = (int) (file_info->fh & INT_MAX);
wf_impl_jsonrpc_proxy_notify(rpc, "close", "iii", inode, handle, file_info->flags);
wf_impl_jsonrpc_proxy_notify(rpc, "close", "siii", user_data->name, inode, handle, file_info->flags);
}
fuse_reply_err(request, 0);

View File

@ -81,7 +81,7 @@ void wf_impl_operation_getattr (
{
struct fuse_ctx const * context = fuse_req_ctx(request);
struct wf_impl_operations_context * user_data = fuse_req_userdata(request);
struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, inode);
struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data);
if (NULL != rpc)
{
@ -91,7 +91,7 @@ void wf_impl_operation_getattr (
getattr_context->gid = context->gid;
getattr_context->timeout = user_data->timeout;
wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_getattr_finished, getattr_context, "getattr", "i", inode);
wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_getattr_finished, getattr_context, "getattr", "si", user_data->name, inode);
}
else
{

View File

@ -90,7 +90,7 @@ void wf_impl_operation_lookup (
{
struct fuse_ctx const * context = fuse_req_ctx(request);
struct wf_impl_operations_context * user_data = fuse_req_userdata(request);
struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, parent);
struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data);
if (NULL != rpc)
{
@ -100,7 +100,7 @@ void wf_impl_operation_lookup (
lookup_context->gid = context->gid;
lookup_context->timeout = user_data->timeout;
wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_lookup_finished, lookup_context, "lookup", "is", (int) (parent & INT_MAX), name);
wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_lookup_finished, lookup_context, "lookup", "sis", user_data->name, (int) (parent & INT_MAX), name);
}
else
{

View File

@ -47,11 +47,11 @@ void wf_impl_operation_open(
struct fuse_file_info * file_info)
{
struct wf_impl_operations_context * user_data = fuse_req_userdata(request);
struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, inode);
struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data);
if (NULL != rpc)
{
wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_open_finished, request, "open", "ii", inode, file_info->flags);
wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_open_finished, request, "open", "sii", user_data->name, inode, file_info->flags);
}
else
{

View File

@ -87,13 +87,13 @@ void wf_impl_operation_read(
struct fuse_file_info * file_info)
{
struct wf_impl_operations_context * user_data = fuse_req_userdata(request);
struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, inode);
struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data);
if (NULL != rpc)
{
int const length = (size <= WF_MAX_READ_LENGTH) ? (int) size : WF_MAX_READ_LENGTH;
int handle = (file_info->fh & INT_MAX);
wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_read_finished, request, "read", "iiii", inode, handle, (int) offset, length);
wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_read_finished, request, "read", "siiii", user_data->name, inode, handle, (int) offset, length);
}
else
{

View File

@ -137,7 +137,7 @@ void wf_impl_operation_readdir (
struct fuse_file_info * WF_UNUSED_PARAM(file_info))
{
struct wf_impl_operations_context * user_data = fuse_req_userdata(request);
struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, inode);
struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data);
if (NULL != rpc)
{
@ -146,7 +146,7 @@ void wf_impl_operation_readdir (
readdir_context->size = size;
readdir_context->offset = offset;
wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_readdir_finished, readdir_context, "readdir", "i", inode);
wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_readdir_finished, readdir_context, "readdir", "si", user_data->name, inode);
}
else
{

View File

@ -4,13 +4,11 @@
#include <stddef.h>
struct wf_impl_jsonrpc_proxy * wf_impl_operations_context_get_proxy(
struct wf_impl_operations_context * context,
fuse_ino_t inode)
struct wf_impl_operations_context * context)
{
struct wf_impl_jsonrpc_proxy * proxy = NULL;
struct wf_impl_session_manager * session_manger = context->session_manager;
struct wf_impl_session * session = wf_impl_session_manager_get_by_inode(session_manger, inode);
struct wf_impl_session * session = context->session;
if (NULL != session)
{
proxy = &session->rpc;

View File

@ -7,13 +7,14 @@
extern "C" {
#endif
struct wf_impl_session_manager;
struct wf_impl_session;
struct wf_impl_jsonrpc_proxy;
struct wf_impl_operations_context
{
struct wf_impl_session_manager * session_manager;
struct wf_impl_session * session;
double timeout;
char * name;
};
extern void wf_impl_operation_lookup (
@ -49,8 +50,7 @@ extern void wf_impl_operation_read(
struct fuse_file_info *fi);
extern struct wf_impl_jsonrpc_proxy * wf_impl_operations_context_get_proxy(
struct wf_impl_operations_context * context,
fuse_ino_t inode);
struct wf_impl_operations_context * context);
#ifdef __cplusplus
}

View File

@ -108,18 +108,11 @@ struct wf_server * wf_impl_server_create(
server = malloc(sizeof(struct wf_server));
if (NULL != server)
{
if (wf_impl_server_protocol_init(&server->protocol, config->mount_point))
{
server->shutdown_requested = false;
wf_impl_server_config_clone(config, &server->config);
wf_impl_authenticators_move(&server->config.authenticators, &server->protocol.authenticators);
server->context = wf_impl_server_context_create(server);
}
else
{
free(server);
server = NULL;
}
wf_impl_server_protocol_init(&server->protocol, config->mount_point);
server->shutdown_requested = false;
wf_impl_server_config_clone(config, &server->config);
wf_impl_authenticators_move(&server->config.authenticators, &server->protocol.authenticators);
server->context = wf_impl_server_context_create(server);
}
}

View File

@ -1,12 +1,12 @@
#include "webfuse/adapter/impl/server_protocol.h"
#include <stdlib.h>
#include <ctype.h>
#include <libwebsockets.h>
#include "webfuse/core/message.h"
#include "webfuse/core/util.h"
#include "webfuse/adapter/impl/filesystem.h"
#include "webfuse/adapter/impl/credentials.h"
#include "webfuse/adapter/impl/jsonrpc/request.h"
@ -25,23 +25,14 @@ static int wf_impl_server_protocol_callback(
switch (reason)
{
case LWS_CALLBACK_PROTOCOL_INIT:
{
lws_sock_file_fd_type fd;
fd.filefd = wf_impl_filesystem_get_fd(&protocol->filesystem);
if (!lws_adopt_descriptor_vhost(lws_get_vhost(wsi), LWS_ADOPT_RAW_FILE_DESC, fd, ws_protocol->name, NULL))
{
fprintf(stderr, "error: unable to adopt fd");
}
}
break;
case LWS_CALLBACK_ESTABLISHED:
session = wf_impl_session_manager_add(
&protocol->session_manager,
wsi,
&protocol->authenticators,
&protocol->timeout_manager,
&protocol->server);
&protocol->server,
protocol->mount_point);
if (NULL != session)
{
@ -64,7 +55,10 @@ static int wf_impl_server_protocol_callback(
}
break;
case LWS_CALLBACK_RAW_RX_FILE:
wf_impl_filesystem_process_request(&protocol->filesystem);
if (NULL != session)
{
wf_impl_session_process_filesystem_request(session, wsi);
}
break;
default:
break;
@ -79,11 +73,7 @@ struct wf_server_protocol * wf_impl_server_protocol_create(
struct wf_server_protocol * protocol = malloc(sizeof(struct wf_server_protocol));
if (NULL != protocol)
{
if (!wf_impl_server_protocol_init(protocol, mount_point))
{
free(protocol);
protocol = NULL;
}
wf_impl_server_protocol_init(protocol, mount_point);
}
return protocol;
@ -140,35 +130,91 @@ static void wf_impl_server_protocol_authenticate(
}
}
bool wf_impl_server_protocol_init(
static bool wf_impl_server_protocol_check_name(char const * value)
{
while ('\0' != *value)
{
char const c = * value;
if (!isalpha(c) && !isdigit(c) && ('_' != c))
{
return false;
}
value++;
}
return true;
}
static void wf_impl_server_protocol_add_filesystem(
struct wf_impl_jsonrpc_request * request,
char const * WF_UNUSED_PARAM(method_name),
json_t * params,
void * WF_UNUSED_PARAM(user_data))
{
struct wf_impl_session * session = wf_impl_jsonrpc_request_get_userdata(request);
wf_status status = (session->is_authenticated) ? WF_GOOD : WF_BAD_ACCESS_DENIED;
char const * name = NULL;
if (WF_GOOD == status)
{
json_t * name_holder = json_array_get(params, 0);
if (json_is_string(name_holder))
{
name = json_string_value(name_holder);
if (wf_impl_server_protocol_check_name(name))
{
bool const success = wf_impl_session_add_filesystem(session, name);
if (!success)
{
status = WF_BAD;
}
}
else
{
status = WF_BAD_FORMAT;
}
}
else
{
status = WF_BAD_FORMAT;
}
}
if (WF_GOOD == status)
{
json_t * result = json_object();
json_object_set_new(result, "id", json_string(name));
wf_impl_jsonrpc_respond(request, result);
}
else
{
wf_impl_jsonrpc_respond_error(request, status);
}
}
void wf_impl_server_protocol_init(
struct wf_server_protocol * protocol,
char * mount_point)
{
protocol->mount_point = strdup(mount_point);
wf_impl_timeout_manager_init(&protocol->timeout_manager);
wf_impl_session_manager_init(&protocol->session_manager);
wf_impl_authenticators_init(&protocol->authenticators);
wf_impl_jsonrpc_server_init(&protocol->server);
wf_impl_jsonrpc_server_add(&protocol->server, "authenticate", &wf_impl_server_protocol_authenticate, protocol);
bool const success = wf_impl_filesystem_init(&protocol->filesystem, &protocol->session_manager, mount_point);
// cleanup on error
if (!success)
{
wf_impl_jsonrpc_server_cleanup(&protocol->server);
wf_impl_authenticators_cleanup(&protocol->authenticators);
wf_impl_timeout_manager_cleanup(&protocol->timeout_manager);
wf_impl_session_manager_cleanup(&protocol->session_manager);
}
return success;
wf_impl_jsonrpc_server_add(&protocol->server, "add_filesystem", &wf_impl_server_protocol_add_filesystem, protocol);
}
void wf_impl_server_protocol_cleanup(
struct wf_server_protocol * protocol)
{
wf_impl_filesystem_cleanup(&protocol->filesystem);
free(protocol->mount_point);
wf_impl_jsonrpc_server_cleanup(&protocol->server);
wf_impl_timeout_manager_cleanup(&protocol->timeout_manager);
wf_impl_authenticators_cleanup(&protocol->authenticators);

View File

@ -1,7 +1,6 @@
#ifndef WF_ADAPTER_IMPL_SERVER_PROTOCOL_H
#define WF_ADAPTER_IMPL_SERVER_PROTOCOL_H
#include "webfuse/adapter/impl/filesystem.h"
#include "webfuse/adapter/impl/jsonrpc/proxy.h"
#include "webfuse/adapter/impl/time/timeout_manager.h"
#include "webfuse/adapter/impl/authenticators.h"
@ -17,14 +16,14 @@ struct lws_protocols;
struct wf_server_protocol
{
char * mount_point;
struct wf_impl_timeout_manager timeout_manager;
struct wf_impl_filesystem filesystem;
struct wf_impl_authenticators authenticators;
struct wf_impl_session_manager session_manager;
struct wf_impl_jsonrpc_server server;
};
extern bool wf_impl_server_protocol_init(
extern void wf_impl_server_protocol_init(
struct wf_server_protocol * protocol,
char * mount_point);

View File

@ -6,8 +6,12 @@
#include "webfuse/adapter/impl/jsonrpc/request.h"
#include "webfuse/adapter/impl/jsonrpc/response.h"
#include "webfuse/core/container_of.h"
#include "webfuse/core/util.h"
#include <libwebsockets.h>
#include <stddef.h>
#include <stdlib.h>
static bool wf_impl_session_send(
json_t * request,
@ -20,7 +24,7 @@ static bool wf_impl_session_send(
if (result)
{
wf_message_queue_push(&session->queue, message);
wf_slist_append(&session->messages, &message->item);
lws_callback_on_writable(session->wsi);
result = true;
@ -33,31 +37,59 @@ static bool wf_impl_session_send(
return result;
}
void wf_impl_session_init(
struct wf_impl_session * session,
struct wf_impl_session * wf_impl_session_create(
struct lws * wsi,
struct wf_impl_authenticators * authenticators,
struct wf_impl_timeout_manager * timeout_manager,
struct wf_impl_jsonrpc_server * server)
{
session->wsi = wsi;
session->is_authenticated = false;
session->authenticators = authenticators;
session->server = server;
wf_impl_jsonrpc_proxy_init(&session->rpc, timeout_manager, &wf_impl_session_send, session);
wf_message_queue_init(&session->queue);
}
struct wf_impl_jsonrpc_server * server,
char const * mount_point)
{
void wf_impl_session_cleanup(
struct wf_impl_session * session = malloc(sizeof(struct wf_impl_session));
if (NULL != session)
{
wf_slist_init(&session->filesystems);
session->mount_point = strdup(mount_point);
session->wsi = wsi;
session->is_authenticated = false;
session->authenticators = authenticators;
session->server = server;
wf_impl_jsonrpc_proxy_init(&session->rpc, timeout_manager, &wf_impl_session_send, session);
wf_slist_init(&session->messages);
}
return session;
}
static void wf_impl_session_dispose_filesystems(
struct wf_slist * filesystems)
{
struct wf_slist_item * item = filesystems->first;
while (NULL != item)
{
struct wf_slist_item * next = item->next;
struct wf_impl_filesystem * filesystem = WF_CONTAINER_OF(item, struct wf_impl_filesystem, item);
wf_impl_filesystem_dispose(filesystem);
item = next;
}
}
void wf_impl_session_dispose(
struct wf_impl_session * session)
{
wf_impl_session_dispose_filesystems(&session->filesystems);
wf_impl_jsonrpc_proxy_cleanup(&session->rpc);
wf_message_queue_cleanup(&session->queue);
wf_message_queue_cleanup(&session->messages);
session->is_authenticated = false;
session->wsi = NULL;
session->authenticators = NULL;
session->server = NULL;
}
free(session->mount_point);
free(session);
}
bool wf_impl_session_authenticate(
struct wf_impl_session * session,
@ -68,16 +100,27 @@ bool wf_impl_session_authenticate(
return session->is_authenticated;
}
bool wf_impl_session_add_filesystem(
struct wf_impl_session * session,
char const * name)
{
struct wf_impl_filesystem * filesystem = wf_impl_filesystem_create(session, name);
wf_slist_append(&session->filesystems, &filesystem->item);
return (NULL != filesystem);
}
void wf_impl_session_onwritable(
struct wf_impl_session * session)
{
if (!wf_message_queue_empty(&session->queue))
{
struct wf_message * message = wf_message_queue_pop(&session->queue);
if (!wf_slist_empty(&session->messages))
{
struct wf_slist_item * item = wf_slist_remove_first(&session->messages);
struct wf_message * message = WF_CONTAINER_OF(item, struct wf_message, item);
lws_write(session->wsi, (unsigned char*) message->data, message->length, LWS_WRITE_TEXT);
wf_message_dispose(message);
if (!wf_message_queue_empty(&session->queue))
if (!wf_slist_empty(&session->messages))
{
lws_callback_on_writable(session->wsi);
}
@ -105,4 +148,48 @@ void wf_impl_session_receive(
json_decref(message);
}
}
}
static struct wf_impl_filesystem * wf_impl_session_get_filesystem(
struct wf_impl_session * session,
struct lws * wsi)
{
struct wf_impl_filesystem * result = NULL;
struct wf_slist_item * item = session->filesystems.first;
while (NULL != item)
{
struct wf_slist_item * next = item->next;
struct wf_impl_filesystem * filesystem = WF_CONTAINER_OF(session->filesystems.first, struct wf_impl_filesystem, item);
if (wsi == filesystem->wsi)
{
result = filesystem;
break;
}
item = next;
}
return result;
}
bool wf_impl_session_contains_wsi(
struct wf_impl_session * session,
struct lws * wsi)
{
bool const result = (NULL != wsi) && ((wsi == session->wsi) || (NULL != wf_impl_session_get_filesystem(session, wsi)));
return result;
}
void wf_impl_session_process_filesystem_request(
struct wf_impl_session * session,
struct lws * wsi)
{
struct wf_impl_filesystem * filesystem = wf_impl_session_get_filesystem(session, wsi);
if (NULL != filesystem)
{
wf_impl_filesystem_process_request(filesystem);
}
}

View File

@ -12,6 +12,8 @@ using std::size_t;
#include "webfuse/core/message_queue.h"
#include "webfuse/adapter/impl/jsonrpc/proxy.h"
#include "webfuse/adapter/impl/jsonrpc/server.h"
#include "webfuse/adapter/impl/filesystem.h"
#include "webfuse/core/slist.h"
#ifdef __cplusplus
extern "C"
@ -26,25 +28,35 @@ struct wf_impl_timeout_manager;
struct wf_impl_session
{
struct wf_slist_item item;
char * mount_point;
struct lws * wsi;
bool is_authenticated;
struct wf_message_queue queue;
struct wf_slist messages;
struct wf_impl_authenticators * authenticators;
struct wf_impl_jsonrpc_server * server;
struct wf_impl_jsonrpc_proxy rpc;
struct wf_slist filesystems;
};
extern void wf_impl_session_init(
struct wf_impl_session * session,
extern struct wf_impl_session * wf_impl_session_create(
struct lws * wsi,
struct wf_impl_authenticators * authenticators,
struct wf_impl_timeout_manager * timeout_manager,
struct wf_impl_jsonrpc_server * server);
struct wf_impl_authenticators * authenticators,
struct wf_impl_timeout_manager * timeout_manager,
struct wf_impl_jsonrpc_server * server,
char const * mount_point);
extern void wf_impl_session_dispose(
struct wf_impl_session * session);
extern bool wf_impl_session_authenticate(
struct wf_impl_session * session,
struct wf_credentials * creds);
extern bool wf_impl_session_add_filesystem(
struct wf_impl_session * session,
char const * name);
extern void wf_impl_session_receive(
struct wf_impl_session * session,
char const * data,
@ -53,8 +65,14 @@ extern void wf_impl_session_receive(
extern void wf_impl_session_onwritable(
struct wf_impl_session * session);
extern void wf_impl_session_cleanup(
struct wf_impl_session * session);
extern bool wf_impl_session_contains_wsi(
struct wf_impl_session * session,
struct lws * wsi);
extern void wf_impl_session_process_filesystem_request(
struct wf_impl_session * session,
struct lws * wsi);
#ifdef __cplusplus
}

View File

@ -1,17 +1,26 @@
#include "webfuse/adapter/impl/session_manager.h"
#include "webfuse/core/util.h"
#include "webfuse/core/container_of.h"
#include <stddef.h>
void wf_impl_session_manager_init(
struct wf_impl_session_manager * manager)
{
wf_impl_session_init(&manager->session, NULL, NULL, NULL, NULL);
wf_slist_init(&manager->sessions);
}
void wf_impl_session_manager_cleanup(
struct wf_impl_session_manager * manager)
{
wf_impl_session_cleanup(&manager->session);
struct wf_slist_item * item = manager->sessions.first;
while (NULL != item)
{
struct wf_slist_item * next = item->next;
struct wf_impl_session * session = WF_CONTAINER_OF(item, struct wf_impl_session, item);
wf_impl_session_dispose(session);
item = next;
}
}
struct wf_impl_session * wf_impl_session_manager_add(
@ -19,13 +28,14 @@ struct wf_impl_session * wf_impl_session_manager_add(
struct lws * wsi,
struct wf_impl_authenticators * authenticators,
struct wf_impl_timeout_manager * timeout_manager,
struct wf_impl_jsonrpc_server * server)
struct wf_impl_jsonrpc_server * server,
char const * mount_point)
{
struct wf_impl_session * session = NULL;
if (NULL == manager->session.wsi)
struct wf_impl_session * session = wf_impl_session_create(
wsi, authenticators, timeout_manager, server, mount_point);
if (NULL != session)
{
session = &manager->session;
wf_impl_session_init(&manager->session, wsi, authenticators, timeout_manager, server);
wf_slist_append(&manager->sessions, &session->item);
}
return session;
@ -36,37 +46,42 @@ struct wf_impl_session * wf_impl_session_manager_get(
struct lws * wsi)
{
struct wf_impl_session * session = NULL;
if (wsi == manager->session.wsi)
struct wf_slist_item * item = manager->sessions.first;
while (NULL != item)
{
session = &manager->session;
struct wf_slist_item * next = item->next;
struct wf_impl_session * current = WF_CONTAINER_OF(item, struct wf_impl_session, item);
if (wf_impl_session_contains_wsi(current, wsi)) {
session = current;
break;
}
item = next;
}
return session;
}
struct wf_impl_session * wf_impl_session_manager_get_by_inode(
struct wf_impl_session_manager * manager,
fuse_ino_t WF_UNUSED_PARAM(inode))
{
// ToDo: use inode to determine session manager
struct wf_impl_session * session = NULL;
if (NULL != manager->session.wsi)
{
session = &manager->session;
}
return session;
}
void wf_impl_session_manager_remove(
struct wf_impl_session_manager * manager,
struct lws * wsi)
{
if (wsi == manager->session.wsi)
struct wf_slist_item * item = manager->sessions.first;
struct wf_slist_item * prev = NULL;
while (NULL != item)
{
wf_impl_session_cleanup(&manager->session);
manager->session.wsi = NULL;
struct wf_slist_item * next = item->next;
struct wf_impl_session * session = WF_CONTAINER_OF(item, struct wf_impl_session, item);
if (wsi == session->wsi)
{
wf_slist_remove_after(&manager->sessions, prev);
wf_impl_session_dispose(session);
break;
}
prev = item;
item = next;
}
}

View File

@ -7,6 +7,7 @@
#include "webfuse/adapter/impl/session.h"
#include "webfuse/adapter/impl/fuse_wrapper.h"
#include "webfuse/core/slist.h"
#ifdef __cplusplus
extern "C"
@ -19,7 +20,7 @@ struct wf_impl_jsonrpc_server;
struct wf_impl_session_manager
{
struct wf_impl_session session;
struct wf_slist sessions;
};
extern void wf_impl_session_manager_init(
@ -33,16 +34,13 @@ extern struct wf_impl_session * wf_impl_session_manager_add(
struct lws * wsi,
struct wf_impl_authenticators * authenticators,
struct wf_impl_timeout_manager * timeout_manager,
struct wf_impl_jsonrpc_server * server);
struct wf_impl_jsonrpc_server * server,
char const * mount_point);
extern struct wf_impl_session * wf_impl_session_manager_get(
struct wf_impl_session_manager * manager,
struct lws * wsi);
extern struct wf_impl_session * wf_impl_session_manager_get_by_inode(
struct wf_impl_session_manager * session_manger,
fuse_ino_t inode);
extern void wf_impl_session_manager_remove(
struct wf_impl_session_manager * manager,
struct lws * wsi);

View File

@ -0,0 +1,13 @@
#ifndef WF_CONTAINER_OF_H
#define WF_CONTAINER_OF_H
#ifndef __cplusplus
#include <stddef.h>
#else
#include <cstddef>
#endif
#define WF_CONTAINER_OF(pointer, type, member) \
(type *) (((char *) pointer) - offsetof(type, member))
#endif

View File

@ -16,7 +16,6 @@ extern struct wf_message * wf_message_create(json_t const * value)
{
message->data = &data[sizeof(struct wf_message) + LWS_PRE];
message->length = length;
message->next = NULL;
json_dumpb(value, message->data, length, JSON_COMPACT);
}

View File

@ -9,10 +9,11 @@ using std::size_t;
#endif
#include <jansson.h>
#include "webfuse/core/slist.h"
struct wf_message
{
struct wf_message * next;
struct wf_slist_item item;
char * data;
size_t length;
};
@ -22,7 +23,8 @@ extern "C"
{
#endif
extern struct wf_message * wf_message_create(json_t const * value);
extern struct wf_message * wf_message_create(
json_t const * value);
extern void wf_message_dispose(
struct wf_message * message);

View File

@ -1,63 +1,17 @@
#include "webfuse/core/message_queue.h"
#include "webfuse/core/message.h"
void wf_message_queue_init(
struct wf_message_queue * queue)
{
queue->first = NULL;
queue->last = NULL;
}
#include "webfuse/core/container_of.h"
void wf_message_queue_cleanup(
struct wf_message_queue * queue)
struct wf_slist * queue)
{
struct wf_message * message = queue->first;
while (NULL != message)
struct wf_slist_item * item = queue->first;
while (NULL != item)
{
struct wf_message * next = message->next;
struct wf_slist_item * next = item->next;
struct wf_message * message = WF_CONTAINER_OF(item, struct wf_message, item);
wf_message_dispose(message);
message = next;
item = next;
}
wf_message_queue_init(queue);
}
bool wf_message_queue_empty(
struct wf_message_queue * queue)
{
return (NULL == queue->first);
}
void wf_message_queue_push(
struct wf_message_queue * queue,
struct wf_message * message)
{
message->next = NULL;
if (NULL != queue->last)
{
queue->last->next = message;
queue->last = message;
}
else
{
queue->first = message;
queue->last = message;
}
}
struct wf_message * wf_message_queue_pop(
struct wf_message_queue * queue)
{
struct wf_message * const result = queue->first;
if (NULL != result)
{
queue->first = queue->first->next;
if (NULL == queue->first)
{
queue->last = NULL;
}
}
return result;
wf_slist_init(queue);
}

View File

@ -1,39 +1,16 @@
#ifndef WF_MESSAGE_QUEUE_H
#define WF_MESSAGE_QUEUE_H
#ifndef __cplusplus
#include <stdbool.h>
#endif
struct wf_message_queue;
struct wf_message;
struct wf_message_queue
{
struct wf_message * first;
struct wf_message * last;
};
#ifdef __cplusplus
extern "C"
{
#endif
extern void wf_message_queue_init(
struct wf_message_queue * queue);
struct wf_slist;
extern void wf_message_queue_cleanup(
struct wf_message_queue * queue);
struct wf_slist * queue);
extern bool wf_message_queue_empty(
struct wf_message_queue * queue);
extern void wf_message_queue_push(
struct wf_message_queue * queue,
struct wf_message * message);
extern struct wf_message * wf_message_queue_pop(
struct wf_message_queue * queue);
#ifdef __cplusplus
}

77
lib/webfuse/core/slist.c Normal file
View File

@ -0,0 +1,77 @@
#include "webfuse/core/slist.h"
#include <stddef.h>
void wf_slist_init(
struct wf_slist * list)
{
list->first = NULL;
list->last = NULL;
}
bool wf_slist_empty(
struct wf_slist * list)
{
return (NULL == list->first);
}
void wf_slist_append(
struct wf_slist * list,
struct wf_slist_item * item)
{
item->next = NULL;
if (NULL != list->last)
{
list->last->next = item;
list->last = item;
}
else
{
list->first = item;
list->last = item;
}
}
struct wf_slist_item * wf_slist_remove_first(
struct wf_slist * list)
{
struct wf_slist_item * const result = list->first;
if (NULL != result)
{
list->first = list->first->next;
if (NULL == list->first)
{
list->last = NULL;
}
}
return result;
}
struct wf_slist_item * wf_slist_remove_after(
struct wf_slist * list,
struct wf_slist_item * prev)
{
struct wf_slist_item * result = NULL;
if (NULL != prev)
{
result = prev->next;
if ((NULL != result) && (NULL != result->next))
{
prev->next = result->next;
}
else
{
list->last = prev;
prev->next = NULL;
}
}
else
{
result = wf_slist_remove_first(list);
}
return result;
}

45
lib/webfuse/core/slist.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef WF_SLIST_H
#define WF_SLIST_H
#ifndef __cplusplus
#include <stdbool.h>
#endif
#ifdef __cplusplus
extern "C"
{
#endif
struct wf_slist_item
{
struct wf_slist_item * next;
};
struct wf_slist
{
struct wf_slist_item * first;
struct wf_slist_item * last;
};
extern void wf_slist_init(
struct wf_slist * list);
extern bool wf_slist_empty(
struct wf_slist * list);
extern void wf_slist_append(
struct wf_slist * list,
struct wf_slist_item * item);
extern struct wf_slist_item * wf_slist_remove_first(
struct wf_slist * list);
extern struct wf_slist_item * wf_slist_remove_after(
struct wf_slist * list,
struct wf_slist_item * prev);
#ifdef __cplusplus
}
#endif
#endif

35
lib/webfuse/core/string.c Normal file
View File

@ -0,0 +1,35 @@
#include "webfuse/core/string.h"
#include <stdio.h>
#include <stdlib.h>
char * wf_create_string(char const * format, ...)
{
char * result = NULL;
va_list measure_args;
va_start(measure_args, format);
char buffer;
int needed = vsnprintf(&buffer, 1, format, measure_args); /* Flawfinder: ignore */
va_end(measure_args);
if (0 <= needed)
{
result = malloc(needed + 1);
if (NULL != result)
{
va_list args;
va_start(args, format);
int count = vsnprintf(result, needed + 1, format, args); /* Flawfinder: ignore */
va_end(args);
if ((count < 0) || (needed < count))
{
free(result);
result = NULL;
}
}
}
return result;
}

21
lib/webfuse/core/string.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef WF_CORE_STRING_H
#define WF_CORE_STRING_H
#ifndef __cplusplus
#include <stdarg.h>
#else
#include <cstdarg>
#endif
#ifdef __cplusplus
extern "C"
{
#endif
extern char * wf_create_string(char const * format, ...);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -10,6 +10,8 @@
#include "webfuse/provider/impl/provider.h"
#include "webfuse/core/util.h"
#include "webfuse/core/message.h"
#include "webfuse/core/message_queue.h"
#include "webfuse/core/container_of.h"
static void wfp_impl_client_protocol_respond(
json_t * response,
@ -20,7 +22,7 @@ static void wfp_impl_client_protocol_respond(
struct wf_message * message = wf_message_create(response);
if (NULL != message)
{
wf_message_queue_push(&protocol->queue, message);
wf_slist_append(&protocol->messages, &message->item);
lws_callback_on_writable(protocol->wsi);
}
}
@ -45,6 +47,26 @@ static void wfp_impl_client_protocol_process_request(
}
}
static void wfp_impl_client_protocol_add_filesystem(
struct wfp_client_protocol * protocol)
{
json_t * params = json_array();
json_array_append_new(params, json_string("cprovider"));
json_t * request = json_object();
json_object_set_new(request, "method", json_string("add_filesystem"));
json_object_set_new(request, "params", params);
json_object_set_new(request, "id", json_integer(42));
struct wf_message * message = wf_message_create(request);
if (NULL != message)
{
wf_slist_append(&protocol->messages, &message->item);
lws_callback_on_writable(protocol->wsi);
}
json_decref(request);
}
static int wfp_impl_client_protocol_callback(
struct lws * wsi,
@ -61,6 +83,7 @@ static int wfp_impl_client_protocol_callback(
switch (reason)
{
case LWS_CALLBACK_CLIENT_ESTABLISHED:
wfp_impl_client_protocol_add_filesystem(protocol);
protocol->provider.connected(protocol->user_data);
break;
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
@ -75,13 +98,14 @@ static int wfp_impl_client_protocol_callback(
case LWS_CALLBACK_SERVER_WRITEABLE:
// fall-through
case LWS_CALLBACK_CLIENT_WRITEABLE:
if ((wsi == protocol->wsi) && (!wf_message_queue_empty(&protocol->queue)))
if ((wsi == protocol->wsi) && (!wf_slist_empty(&protocol->messages)))
{
struct wf_message * message = wf_message_queue_pop(&protocol->queue);
struct wf_slist_item * item = wf_slist_remove_first(&protocol->messages);
struct wf_message * message = WF_CONTAINER_OF(item, struct wf_message, item);
lws_write(wsi, (unsigned char*) message->data, message->length, LWS_WRITE_TEXT);
wf_message_dispose(message);
if (!wf_message_queue_empty(&protocol->queue))
if (!wf_slist_empty(&protocol->messages))
{
lws_callback_on_writable(wsi);
@ -102,7 +126,7 @@ void wfp_impl_client_protocol_init(
struct wfp_provider const * provider,
void * user_data)
{
wf_message_queue_init(&protocol->queue);
wf_slist_init(&protocol->messages);
protocol->wsi = NULL;
@ -116,7 +140,7 @@ void wfp_impl_client_protocol_init(
void wfp_impl_client_protocol_cleanup(
struct wfp_client_protocol * protocol)
{
wf_message_queue_cleanup(&protocol->queue);
wf_message_queue_cleanup(&protocol->messages);
}
struct wfp_client_protocol * wfp_impl_client_protocol_create(

View File

@ -4,7 +4,7 @@
#include "webfuse/provider/impl/provider.h"
#include "webfuse/provider/impl/request.h"
#include "webfuse/core/message_queue.h"
#include "webfuse/core/slist.h"
#ifdef __cplusplus
extern "C"
@ -20,7 +20,7 @@ struct wfp_client_protocol
struct wfp_provider provider;
void * user_data;
struct lws * wsi;
struct wf_message_queue queue;
struct wf_slist messages;
};
extern void wfp_impl_client_protocol_init(

View File

@ -8,11 +8,11 @@ void wfp_impl_close(
int WF_UNUSED_PARAM(id))
{
size_t const param_count = json_array_size(params);
if (3 == param_count)
if (4 == param_count)
{
json_t * inode_holder = json_array_get(params, 0);
json_t * handle_holder = json_array_get(params, 1);
json_t * flags_holder = json_array_get(params, 2);
json_t * inode_holder = json_array_get(params, 1);
json_t * handle_holder = json_array_get(params, 2);
json_t * flags_holder = json_array_get(params, 3);
if (json_is_integer(inode_holder) &&
json_is_integer(handle_holder) &&

View File

@ -13,9 +13,9 @@ void wfp_impl_getattr(
int id)
{
size_t const count = json_array_size(params);
if (1 == count)
if (2 == count)
{
json_t * inode_holder = json_array_get(params, 0);
json_t * inode_holder = json_array_get(params, 1);
if (json_is_integer(inode_holder))
{

View File

@ -12,10 +12,10 @@ void wfp_impl_lookup(
int id)
{
size_t const count = json_array_size(params);
if (2 == count)
if (3 == count)
{
json_t * inode_holder = json_array_get(params, 0);
json_t * name_holder = json_array_get(params, 1);
json_t * inode_holder = json_array_get(params, 1);
json_t * name_holder = json_array_get(params, 2);
if (json_is_integer(inode_holder) &&
json_is_string(name_holder))

View File

@ -9,10 +9,10 @@ void wfp_impl_open(
int id)
{
size_t const count = json_array_size(params);
if (2 == count)
if (3 == count)
{
json_t * inode_holder = json_array_get(params, 0);
json_t * flags_holder = json_array_get(params, 1);
json_t * inode_holder = json_array_get(params, 1);
json_t * flags_holder = json_array_get(params, 2);
if (json_is_integer(inode_holder) &&
json_is_integer(flags_holder))

View File

@ -13,12 +13,12 @@ void wfp_impl_read(
int id)
{
size_t const count = json_array_size(params);
if (4 == count)
if (5 == count)
{
json_t * inode_holder = json_array_get(params, 0);
json_t * handle_holder = json_array_get(params, 1);
json_t * offset_holder = json_array_get(params, 2);
json_t * length_holder = json_array_get(params, 3);
json_t * inode_holder = json_array_get(params, 1);
json_t * handle_holder = json_array_get(params, 2);
json_t * offset_holder = json_array_get(params, 3);
json_t * length_holder = json_array_get(params, 4);
if (json_is_integer(inode_holder) &&
json_is_integer(handle_holder) &&

View File

@ -10,9 +10,9 @@ void wfp_impl_readdir(
int id)
{
size_t const count = json_array_size(params);
if (1 == count)
if (2 == count)
{
json_t * inode_holder = json_array_get(params, 0);
json_t * inode_holder = json_array_get(params, 1);
if ((NULL != inode_holder) && (json_is_integer(inode_holder)))
{

29
test/test_container_of.cc Normal file
View File

@ -0,0 +1,29 @@
#include <gtest/gtest.h>
#include "webfuse/core/container_of.h"
namespace
{
struct MyStruct
{
int first;
int second;
};
}
TEST(ContainerOf, FirstMember)
{
MyStruct my_struct = {23, 42};
int * first = &my_struct.first;
ASSERT_EQ(&my_struct, WF_CONTAINER_OF(first, MyStruct, first));
}
TEST(ContainerOf, SecondMember)
{
MyStruct my_struct = {23, 42};
int * second = &my_struct.second;
ASSERT_EQ(&my_struct, WF_CONTAINER_OF(second, MyStruct, second));
}

35
test/test_slist.cc Normal file
View File

@ -0,0 +1,35 @@
#include <gtest/gtest.h>
#include "webfuse/core/slist.h"
TEST(wf_slist_remove_after, RemoveFirst)
{
struct wf_slist list;
struct wf_slist_item item[10];
wf_slist_init(&list);
wf_slist_append(&list, &item[0]);
wf_slist_item * removed = wf_slist_remove_after(&list, NULL);
ASSERT_TRUE(wf_slist_empty(&list));
ASSERT_EQ(nullptr, list.first);
ASSERT_EQ(nullptr, list.last);
ASSERT_EQ(&item[0], removed);
}
TEST(wf_slist_remove_after, RemoveLast)
{
struct wf_slist list;
struct wf_slist_item item[10];
wf_slist_init(&list);
wf_slist_append(&list, &item[0]);
wf_slist_append(&list, &item[1]);
wf_slist_append(&list, &item[2]);
wf_slist_item * removed = wf_slist_remove_after(&list, &item[1]);
ASSERT_FALSE(wf_slist_empty(&list));
ASSERT_EQ(&item[0], list.first);
ASSERT_EQ(&item[1], list.last);
ASSERT_EQ(nullptr, item[1].next);
ASSERT_EQ(&item[2], removed);
}

18
test/test_string.cc Normal file
View File

@ -0,0 +1,18 @@
#include <gtest/gtest.h>
#include <stdlib.h>
#include "webfuse/core/string.h"
TEST(wf_string_create, Default)
{
char * value = wf_create_string("test %s/%d", "hello", 42);
ASSERT_STREQ("test hello/42", value);
free(value);
}
TEST(wf_string_create, EmptyString)
{
char * value = wf_create_string("");
ASSERT_STREQ("", value);
free(value);
}