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(FUSE3 REQUIRED fuse3)
pkg_check_modules(LWS REQUIRED libwebsockets) pkg_check_modules(LWS REQUIRED libwebsockets)
pkg_check_modules(JANSSON REQUIRED jansson) pkg_check_modules(JANSSON REQUIRED jansson)
pkg_check_modules(UUID REQUIRED uuid)
add_definitions(-D_FILE_OFFSET_BITS=64) add_definitions(-D_FILE_OFFSET_BITS=64)
@ -26,6 +27,7 @@ set(EXTRA_INCLUDE_DIRS
${FUSE3_INCLUDE_DIRS} ${FUSE3_INCLUDE_DIRS}
${LWS_INCLUDE_DIRS} ${LWS_INCLUDE_DIRS}
${JANSSON_INCLUDE_DIRS} ${JANSSON_INCLUDE_DIRS}
${UUID_INCLUDE_DIRS}
) )
set(EXTRA_LIBS set(EXTRA_LIBS
@ -33,6 +35,7 @@ set(EXTRA_LIBS
${FUSE3_LIBRARIES} ${FUSE3_LIBRARIES}
${LWS_LIBRARIES} ${LWS_LIBRARIES}
${JANSSON_LIBRARIES} ${JANSSON_LIBRARIES}
${UUID_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT} ${CMAKE_THREAD_LIBS_INIT}
) )
@ -41,6 +44,7 @@ set(EXTRA_CFLAGS
${FUSE3_CFLAGS_OTHER} ${FUSE3_CFLAGS_OTHER}
${LWS_CFLAGS_OTHER} ${LWS_CFLAGS_OTHER}
${JANSSON_CFLAGS_OTHER} ${JANSSON_CFLAGS_OTHER}
${UUID_CFLAGS_OTHER}
"-pthread" "-pthread"
) )
@ -48,9 +52,11 @@ set(EXTRA_CFLAGS
# libwebfuse-core # libwebfuse-core
add_library(webfuse-core STATIC add_library(webfuse-core STATIC
lib/webfuse/core/slist.c
lib/webfuse/core/message.c lib/webfuse/core/message.c
lib/webfuse/core/message_queue.c lib/webfuse/core/message_queue.c
lib/webfuse/core/status.c lib/webfuse/core/status.c
lib/webfuse/core/string.c
) )
set_target_properties(webfuse-core PROPERTIES OUTPUT_NAME webfuse-core) set_target_properties(webfuse-core PROPERTIES OUTPUT_NAME webfuse-core)
@ -272,6 +278,7 @@ pkg_check_modules(GMOCK gmock)
add_executable(alltests add_executable(alltests
test/msleep.cc test/msleep.cc
test/mock_authenticator.cc test/mock_authenticator.cc
test/test_container_of.cc
test/test_response_parser.cc test/test_response_parser.cc
test/test_server.cc test/test_server.cc
test/test_timepoint.cc test/test_timepoint.cc
@ -280,6 +287,8 @@ add_executable(alltests
test/test_credentials.cc test/test_credentials.cc
test/test_authenticator.cc test/test_authenticator.cc
test/test_authenticators.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}) 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. 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: 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. - The websocket filesystem daemon (*webfuse daemon*) waits for incoming connections.
It starts the websocket server and waits for incoming connections.
- A remote filesystem provider connects to webfuse daemon via websocket protocol. - A remote filesystem provider connects to webfuse daemon via websocket protocol and adds one or more filesystems.
The example includes such a provider implemented in HTML and JavaScript. *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 - 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. 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 ## Similar Projects
### Davfs2 ### 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. 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": { fs provider: {"result": {
"inode": <inode>, "inode": <inode>,
"mode" : <mode>, "mode" : <mode>,
@ -154,6 +173,7 @@ Retrieve information about a filesystem entry by name.
| Item | Data type | Description | | Item | Data type | Description |
| ----------- | --------------- | ------------------------------------------- | | ----------- | --------------- | ------------------------------------------- |
| filesystem | string | name of the filesystem |
| parent | integer | inode of parent directory (1 = root) | | parent | integer | inode of parent directory (1 = root) |
| name | string | name of the filesystem object to look up | | name | string | name of the filesystem object to look up |
| inode | integer | inode of the filesystem object | | inode | integer | inode of the filesystem object |
@ -168,7 +188,7 @@ Retrieve information about a filesystem entry by name.
Get file attributes. Get file attributes.
webfuse daemon: {"method": "getattr", "params": [<inode>], "id": <id>} webfuse daemon: {"method": "getattr", "params": [<filesystem>, <inode>], "id": <id>}
fs provider: {"result": { fs provider: {"result": {
"mode" : <mode>, "mode" : <mode>,
"type" : <type>, "type" : <type>,
@ -180,6 +200,7 @@ Get file attributes.
| Item | Data type | Description | | Item | Data type | Description |
| ----------- | --------------- | ------------------------------------------- | | ----------- | --------------- | ------------------------------------------- |
| filesystem | string | name of the filesystem |
| inode | integer | inode of the filesystem object | | inode | integer | inode of the filesystem object |
| mode | integer | unix file mode | | mode | integer | unix file mode |
| type | "file" or "dir" | type of filesystem object | | 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 Result is an array of name-inode pairs for each entry. The generic entries
"." and ".." should also be provided. "." 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": [ fs provider: {"result": [
{"name": <name>, "inode": <inode>}, {"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 | | Item | Data type | Description |
| ----------- | --------------- | ------------------------------ | | ----------- | --------------- | ------------------------------ |
| filesystem | string | name of the filesystem |
| dir_inode | integer | inode of the directory to read | | dir_inode | integer | inode of the directory to read |
| name | integer | name of the entry | | name | integer | name of the entry |
| inode | integer | inode 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. 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>} fs provider: {"result": {"handle": <handle>}, "id": <id>}
| Item | Data type | Description | | Item | Data type | Description |
| ----------- | ----------| ----------------------------- | | ----------- | ----------| ----------------------------- |
| filesystem | string | name of the filesystem |
| inode | integer | inode of the file | | inode | integer | inode of the file |
| flags | integer | access mode flags (see below) | | flags | integer | access mode flags (see below) |
| handle | integer | handle of the file | | handle | integer | handle of the file |
@ -237,10 +260,11 @@ Open a file.
Informs filesystem provider, that a file is closed. Informs filesystem provider, that a file is closed.
Since `close` is a notification, it cannot fail. 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 | | Item | Data type | Description |
| ----------- | ----------| ---------------------------- | | ----------- | ----------| ---------------------------- |
| filesystem | string | name of the filesystem |
| inode | integer | inode of the file | | inode | integer | inode of the file |
| handle | integer | handle of the file | | handle | integer | handle of the file |
| flags | integer | access mode flags (see open) | | flags | integer | access mode flags (see open) |
@ -249,7 +273,7 @@ Since `close` is a notification, it cannot fail.
Read from an open file. 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": { fs provider: {"result": {
"data": <data>, "data": <data>,
"format": <format>, "format": <format>,
@ -258,6 +282,7 @@ Read from an open file.
| Item | Data type | Description | | Item | Data type | Description |
| ----------- | ----------| ----------------------------- | | ----------- | ----------| ----------------------------- |
| filesystem | string | name of the filesystem |
| inode | integer | inode of the file | | inode | integer | inode of the file |
| handle | integer | handle of the file | | handle | integer | handle of the file |
| offset | integer | Offet to start read operation | | offset | integer | Offet to start read operation |
@ -275,10 +300,21 @@ Read from an open file.
### Requests (Provider -> Adapter) ### 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 #### authtenticate
Authenticate the provider. 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>} fs provider: {"method": "authenticate", "params": [<type>, <credentials>], "id": <id>}
webfuse daemon: {"result": {}, "id": <id>} webfuse daemon: {"result": {}, "id": <id>}

View File

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

View File

@ -17,7 +17,8 @@ RUN set -x \
pkg-config \ pkg-config \
rsync \ rsync \
gdb \ gdb \
gdbserver gdbserver \
uuid-dev
COPY src /usr/local/src 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 @startuml
participant "Filesystem Provider\n(e.g. Webbrowser)" as provider participant "Filesystem Provider\n(e.g. Webbrowser)" as provider
participant "webfuse\ndaemon" as daemon participant "webfuse\ndaemon" as daemon
actor "user" as user
group directory listing fails without authentication group add filesystem fails without authentication
user -> daemon : ls provider -> daemon : add_filesystem
daemon -> daemon : is_authenticated daemon -->x provider : error: access denied
daemon -->x user : error: no entry
end end
@ -17,12 +15,10 @@ daemon -> daemon: check(credentials)
daemon --> provider: result daemon --> provider: result
end end
group directory listing succeeds after authentication group add filesystem succeeds after authentication
user -> daemon : ls provider -> daemon: add_filesystem
daemon -> daemon : is_authenticated daemon -> daemon : fuse_mount
daemon -> provider : readdir daemon -> provider: okay
provider --> daemon : readdir_resp
daemon --> user : [., ..]
end end
@enduml @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 actor "user" as user
group startup group startup
daemon -> daemon : fuse_mount
daemon -> daemon : start ws daemon -> daemon : start ws
end end
... ...
@ -16,9 +15,15 @@ provider -> daemon : connect
daemon -> daemon: check credentials daemon -> daemon: check credentials
daemon --> provider: okay daemon --> provider: okay
end end
provider -> daemon: add_filesystem
daemon -> daemon : fuse_mount
daemon -> provider: okay
end end
... ...
group directory listing group directory listing
user -> daemon : ls user -> daemon : ls
daemon -> provider : readdir 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"); char const * password = wf_credentials_get(creds, "password");
if ((NULL != username) && (NULL != password)) if ((NULL != username) && (NULL != password))
{ {
struct userdb * db = userdb_create("<pepper>"); struct userdb * db = userdb_create("");
result = userdb_load(db, args->passwd_path); result = userdb_load(db, args->passwd_path);
if (result) if (result)
{ {
result = userdb_check(db, username, password); result = userdb_check(db, username, password);
userdb_dispose(db);
} }
userdb_dispose(db);
} }
return result; return result;

View File

@ -1,5 +1,6 @@
export class ConnectionView { export class ConnectionView {
constructor(client) { constructor(client, provider) {
this._provider = provider;
this._client = client; this._client = client;
this._client.onopen = () => { this._onConnectionOpened(); }; this._client.onopen = () => { this._onConnectionOpened(); };
this._client.onclose = () => { this._onConnectionClosed(); }; this._client.onclose = () => { this._onConnectionClosed(); };
@ -28,6 +29,14 @@ export class ConnectionView {
const authenticateBox = document.createElement("div"); const authenticateBox = document.createElement("div");
this.element.appendChild(authenticateBox); 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"); const usernameLabel = document.createElement("span");
usernameLabel.textContent = "user:"; usernameLabel.textContent = "user:";
authenticateBox.appendChild(usernameLabel); authenticateBox.appendChild(usernameLabel);
@ -45,12 +54,6 @@ export class ConnectionView {
this.passwordTextbox.type = "password"; this.passwordTextbox.type = "password";
this.passwordTextbox.value = "secret"; this.passwordTextbox.value = "secret";
authenticateBox.appendChild(this.passwordTextbox); 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() { _onConnectButtonClicked() {
@ -65,14 +68,21 @@ export class ConnectionView {
_onAuthenticateButtonClicked() { _onAuthenticateButtonClicked() {
if (this._client.isConnected()) { if (this._client.isConnected()) {
const username = this.usernameTextbox.value;
const password = this.passwordTextbox.value;
this._client.authenticate("username", { username, password });
} }
} }
_onConnectionOpened() { _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"; 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"} "say_hello.sh": { inode: 3, mode: mode("0555"), type: "file", contents: "#!/bin/sh\necho hello\n"}
} }
}); });
const client = new Client(provider); const client = new Client();
const connectionView = new ConnectionView(client); const connectionView = new ConnectionView(client, provider);
document.getElementById('connection').appendChild(connectionView.element); document.getElementById('connection').appendChild(connectionView.element);
} }

View File

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

View File

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

View File

@ -1,5 +1,16 @@
#include "webfuse/adapter/impl/filesystem.h" #include "webfuse/adapter/impl/filesystem.h"
#include "webfuse/adapter/impl/operations.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 <stdlib.h>
#include <stddef.h> #include <stddef.h>
@ -16,23 +27,118 @@ static struct fuse_lowlevel_ops const filesystem_operations =
.read = &wf_impl_operation_read .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( return strdup(id);
struct wf_impl_filesystem * filesystem, }
struct wf_impl_session_manager * session_manager,
char * mount_point) static bool wf_impl_filesystem_is_link_broken(char const * path, char const * id)
{ {
bool result = false; 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}; char * argv[] = {"", NULL};
filesystem->args.argc = 1; filesystem->args.argc = 1;
filesystem->args.argv = argv; filesystem->args.argv = argv;
filesystem->args.allocated = 0; 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.timeout = 1.0;
filesystem->user_data.name = strdup(name);
memset(&filesystem->buffer, 0, sizeof(struct fuse_buf)); 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->session = fuse_session_new(
&filesystem->args, &filesystem->args,
&filesystem_operations, &filesystem_operations,
@ -40,31 +146,50 @@ bool wf_impl_filesystem_init(
&filesystem->user_data); &filesystem->user_data);
if (NULL != filesystem->session) 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; return result;
} }
void wf_impl_filesystem_cleanup( struct wf_impl_filesystem * wf_impl_filesystem_create(
struct wf_impl_filesystem * filesystem) 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); bool success = wf_impl_filesystem_init(filesystem, session, name);
fuse_session_unmount(filesystem->session); if (!success)
fuse_session_destroy(filesystem->session); {
filesystem->session = NULL; free(filesystem);
filesystem = NULL;
}
} }
free(filesystem->buffer.mem); return filesystem;
fuse_opt_free_args(&filesystem->args);
} }
int wf_impl_filesystem_get_fd( void wf_impl_filesystem_dispose(
struct wf_impl_filesystem * filesystem) struct wf_impl_filesystem * filesystem)
{ {
return fuse_session_fd(filesystem->session); wf_impl_filesystem_cleanup(filesystem);
free(filesystem);
} }
void wf_impl_filesystem_process_request( void wf_impl_filesystem_process_request(

View File

@ -7,31 +7,36 @@
#include "webfuse/adapter/impl/fuse_wrapper.h" #include "webfuse/adapter/impl/fuse_wrapper.h"
#include "webfuse/adapter/impl/operations.h" #include "webfuse/adapter/impl/operations.h"
#include "webfuse/core/slist.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
{ {
#endif #endif
struct wf_impl_session_manager; struct wf_impl_session;
struct lws;
struct wf_impl_filesystem struct wf_impl_filesystem
{ {
struct wf_slist_item item;
struct fuse_args args; struct fuse_args args;
struct fuse_session * session; struct fuse_session * session;
struct fuse_buf buffer; struct fuse_buf buffer;
struct wf_impl_operations_context user_data; 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( extern struct wf_impl_filesystem * wf_impl_filesystem_create(
struct wf_impl_filesystem * filesystem, struct wf_impl_session * session,
struct wf_impl_session_manager * session_manager, char const * name);
char * mount_point);
extern void wf_impl_filesystem_cleanup( extern void wf_impl_filesystem_dispose(
struct wf_impl_filesystem * filesystem);
extern int wf_impl_filesystem_get_fd(
struct wf_impl_filesystem * filesystem); struct wf_impl_filesystem * filesystem);
extern void wf_impl_filesystem_process_request( 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 fuse_file_info * file_info)
{ {
struct wf_impl_operations_context * user_data = fuse_req_userdata(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) if (NULL != rpc)
{ {
int handle = (int) (file_info->fh & INT_MAX); 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); 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 fuse_ctx const * context = fuse_req_ctx(request);
struct wf_impl_operations_context * user_data = fuse_req_userdata(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) if (NULL != rpc)
{ {
@ -91,7 +91,7 @@ void wf_impl_operation_getattr (
getattr_context->gid = context->gid; getattr_context->gid = context->gid;
getattr_context->timeout = user_data->timeout; 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 else
{ {

View File

@ -90,7 +90,7 @@ void wf_impl_operation_lookup (
{ {
struct fuse_ctx const * context = fuse_req_ctx(request); struct fuse_ctx const * context = fuse_req_ctx(request);
struct wf_impl_operations_context * user_data = fuse_req_userdata(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) if (NULL != rpc)
{ {
@ -100,7 +100,7 @@ void wf_impl_operation_lookup (
lookup_context->gid = context->gid; lookup_context->gid = context->gid;
lookup_context->timeout = user_data->timeout; 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 else
{ {

View File

@ -47,11 +47,11 @@ void wf_impl_operation_open(
struct fuse_file_info * file_info) struct fuse_file_info * file_info)
{ {
struct wf_impl_operations_context * user_data = fuse_req_userdata(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) 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 else
{ {

View File

@ -87,13 +87,13 @@ void wf_impl_operation_read(
struct fuse_file_info * file_info) struct fuse_file_info * file_info)
{ {
struct wf_impl_operations_context * user_data = fuse_req_userdata(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) if (NULL != rpc)
{ {
int const length = (size <= WF_MAX_READ_LENGTH) ? (int) size : WF_MAX_READ_LENGTH; int const length = (size <= WF_MAX_READ_LENGTH) ? (int) size : WF_MAX_READ_LENGTH;
int handle = (file_info->fh & INT_MAX); 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 else
{ {

View File

@ -137,7 +137,7 @@ void wf_impl_operation_readdir (
struct fuse_file_info * WF_UNUSED_PARAM(file_info)) struct fuse_file_info * WF_UNUSED_PARAM(file_info))
{ {
struct wf_impl_operations_context * user_data = fuse_req_userdata(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) if (NULL != rpc)
{ {
@ -146,7 +146,7 @@ void wf_impl_operation_readdir (
readdir_context->size = size; readdir_context->size = size;
readdir_context->offset = offset; 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 else
{ {

View File

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

View File

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

View File

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

View File

@ -1,12 +1,12 @@
#include "webfuse/adapter/impl/server_protocol.h" #include "webfuse/adapter/impl/server_protocol.h"
#include <stdlib.h> #include <stdlib.h>
#include <ctype.h>
#include <libwebsockets.h> #include <libwebsockets.h>
#include "webfuse/core/message.h" #include "webfuse/core/message.h"
#include "webfuse/core/util.h" #include "webfuse/core/util.h"
#include "webfuse/adapter/impl/filesystem.h"
#include "webfuse/adapter/impl/credentials.h" #include "webfuse/adapter/impl/credentials.h"
#include "webfuse/adapter/impl/jsonrpc/request.h" #include "webfuse/adapter/impl/jsonrpc/request.h"
@ -25,23 +25,14 @@ static int wf_impl_server_protocol_callback(
switch (reason) 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: case LWS_CALLBACK_ESTABLISHED:
session = wf_impl_session_manager_add( session = wf_impl_session_manager_add(
&protocol->session_manager, &protocol->session_manager,
wsi, wsi,
&protocol->authenticators, &protocol->authenticators,
&protocol->timeout_manager, &protocol->timeout_manager,
&protocol->server); &protocol->server,
protocol->mount_point);
if (NULL != session) if (NULL != session)
{ {
@ -64,7 +55,10 @@ static int wf_impl_server_protocol_callback(
} }
break; break;
case LWS_CALLBACK_RAW_RX_FILE: 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; break;
default: default:
break; 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)); struct wf_server_protocol * protocol = malloc(sizeof(struct wf_server_protocol));
if (NULL != protocol) if (NULL != protocol)
{ {
if (!wf_impl_server_protocol_init(protocol, mount_point)) wf_impl_server_protocol_init(protocol, mount_point);
{
free(protocol);
protocol = NULL;
}
} }
return protocol; 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, struct wf_server_protocol * protocol,
char * mount_point) char * mount_point)
{ {
protocol->mount_point = strdup(mount_point);
wf_impl_timeout_manager_init(&protocol->timeout_manager); wf_impl_timeout_manager_init(&protocol->timeout_manager);
wf_impl_session_manager_init(&protocol->session_manager); wf_impl_session_manager_init(&protocol->session_manager);
wf_impl_authenticators_init(&protocol->authenticators); wf_impl_authenticators_init(&protocol->authenticators);
wf_impl_jsonrpc_server_init(&protocol->server); wf_impl_jsonrpc_server_init(&protocol->server);
wf_impl_jsonrpc_server_add(&protocol->server, "authenticate", &wf_impl_server_protocol_authenticate, protocol); wf_impl_jsonrpc_server_add(&protocol->server, "authenticate", &wf_impl_server_protocol_authenticate, protocol);
wf_impl_jsonrpc_server_add(&protocol->server, "add_filesystem", &wf_impl_server_protocol_add_filesystem, 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;
} }
void wf_impl_server_protocol_cleanup( void wf_impl_server_protocol_cleanup(
struct wf_server_protocol * protocol) struct wf_server_protocol * protocol)
{ {
wf_impl_filesystem_cleanup(&protocol->filesystem); free(protocol->mount_point);
wf_impl_jsonrpc_server_cleanup(&protocol->server); wf_impl_jsonrpc_server_cleanup(&protocol->server);
wf_impl_timeout_manager_cleanup(&protocol->timeout_manager); wf_impl_timeout_manager_cleanup(&protocol->timeout_manager);
wf_impl_authenticators_cleanup(&protocol->authenticators); wf_impl_authenticators_cleanup(&protocol->authenticators);

View File

@ -1,7 +1,6 @@
#ifndef WF_ADAPTER_IMPL_SERVER_PROTOCOL_H #ifndef WF_ADAPTER_IMPL_SERVER_PROTOCOL_H
#define 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/jsonrpc/proxy.h"
#include "webfuse/adapter/impl/time/timeout_manager.h" #include "webfuse/adapter/impl/time/timeout_manager.h"
#include "webfuse/adapter/impl/authenticators.h" #include "webfuse/adapter/impl/authenticators.h"
@ -17,14 +16,14 @@ struct lws_protocols;
struct wf_server_protocol struct wf_server_protocol
{ {
char * mount_point;
struct wf_impl_timeout_manager timeout_manager; struct wf_impl_timeout_manager timeout_manager;
struct wf_impl_filesystem filesystem;
struct wf_impl_authenticators authenticators; struct wf_impl_authenticators authenticators;
struct wf_impl_session_manager session_manager; struct wf_impl_session_manager session_manager;
struct wf_impl_jsonrpc_server server; 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, struct wf_server_protocol * protocol,
char * mount_point); char * mount_point);

View File

@ -6,8 +6,12 @@
#include "webfuse/adapter/impl/jsonrpc/request.h" #include "webfuse/adapter/impl/jsonrpc/request.h"
#include "webfuse/adapter/impl/jsonrpc/response.h" #include "webfuse/adapter/impl/jsonrpc/response.h"
#include "webfuse/core/container_of.h"
#include "webfuse/core/util.h"
#include <libwebsockets.h> #include <libwebsockets.h>
#include <stddef.h> #include <stddef.h>
#include <stdlib.h>
static bool wf_impl_session_send( static bool wf_impl_session_send(
json_t * request, json_t * request,
@ -20,7 +24,7 @@ static bool wf_impl_session_send(
if (result) if (result)
{ {
wf_message_queue_push(&session->queue, message); wf_slist_append(&session->messages, &message->item);
lws_callback_on_writable(session->wsi); lws_callback_on_writable(session->wsi);
result = true; result = true;
@ -33,31 +37,59 @@ static bool wf_impl_session_send(
return result; return result;
} }
void wf_impl_session_init( struct wf_impl_session * wf_impl_session_create(
struct wf_impl_session * session,
struct lws * wsi, struct lws * wsi,
struct wf_impl_authenticators * authenticators, struct wf_impl_authenticators * authenticators,
struct wf_impl_timeout_manager * timeout_manager, struct wf_impl_timeout_manager * timeout_manager,
struct wf_impl_jsonrpc_server * server) struct wf_impl_jsonrpc_server * server,
{ char const * 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_message_queue_init(&session->queue);
}
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) struct wf_impl_session * session)
{ {
wf_impl_session_dispose_filesystems(&session->filesystems);
wf_impl_jsonrpc_proxy_cleanup(&session->rpc); wf_impl_jsonrpc_proxy_cleanup(&session->rpc);
wf_message_queue_cleanup(&session->queue); wf_message_queue_cleanup(&session->messages);
session->is_authenticated = false; session->is_authenticated = false;
session->wsi = NULL; session->wsi = NULL;
session->authenticators = NULL; session->authenticators = NULL;
session->server = NULL; session->server = NULL;
} free(session->mount_point);
free(session);
}
bool wf_impl_session_authenticate( bool wf_impl_session_authenticate(
struct wf_impl_session * session, struct wf_impl_session * session,
@ -68,16 +100,27 @@ bool wf_impl_session_authenticate(
return session->is_authenticated; 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( void wf_impl_session_onwritable(
struct wf_impl_session * session) struct wf_impl_session * session)
{ {
if (!wf_message_queue_empty(&session->queue)) if (!wf_slist_empty(&session->messages))
{ {
struct wf_message * message = wf_message_queue_pop(&session->queue); 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); lws_write(session->wsi, (unsigned char*) message->data, message->length, LWS_WRITE_TEXT);
wf_message_dispose(message); wf_message_dispose(message);
if (!wf_message_queue_empty(&session->queue)) if (!wf_slist_empty(&session->messages))
{ {
lws_callback_on_writable(session->wsi); lws_callback_on_writable(session->wsi);
} }
@ -105,4 +148,48 @@ void wf_impl_session_receive(
json_decref(message); 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/core/message_queue.h"
#include "webfuse/adapter/impl/jsonrpc/proxy.h" #include "webfuse/adapter/impl/jsonrpc/proxy.h"
#include "webfuse/adapter/impl/jsonrpc/server.h" #include "webfuse/adapter/impl/jsonrpc/server.h"
#include "webfuse/adapter/impl/filesystem.h"
#include "webfuse/core/slist.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
@ -26,25 +28,35 @@ struct wf_impl_timeout_manager;
struct wf_impl_session struct wf_impl_session
{ {
struct wf_slist_item item;
char * mount_point;
struct lws * wsi; struct lws * wsi;
bool is_authenticated; bool is_authenticated;
struct wf_message_queue queue; struct wf_slist messages;
struct wf_impl_authenticators * authenticators; struct wf_impl_authenticators * authenticators;
struct wf_impl_jsonrpc_server * server; struct wf_impl_jsonrpc_server * server;
struct wf_impl_jsonrpc_proxy rpc; struct wf_impl_jsonrpc_proxy rpc;
struct wf_slist filesystems;
}; };
extern void wf_impl_session_init( extern struct wf_impl_session * wf_impl_session_create(
struct wf_impl_session * session,
struct lws * wsi, struct lws * wsi,
struct wf_impl_authenticators * authenticators, struct wf_impl_authenticators * authenticators,
struct wf_impl_timeout_manager * timeout_manager, struct wf_impl_timeout_manager * timeout_manager,
struct wf_impl_jsonrpc_server * server); 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( extern bool wf_impl_session_authenticate(
struct wf_impl_session * session, struct wf_impl_session * session,
struct wf_credentials * creds); 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( extern void wf_impl_session_receive(
struct wf_impl_session * session, struct wf_impl_session * session,
char const * data, char const * data,
@ -53,8 +65,14 @@ extern void wf_impl_session_receive(
extern void wf_impl_session_onwritable( extern void wf_impl_session_onwritable(
struct wf_impl_session * session); struct wf_impl_session * session);
extern void wf_impl_session_cleanup( extern bool wf_impl_session_contains_wsi(
struct wf_impl_session * session); 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 #ifdef __cplusplus
} }

View File

@ -1,17 +1,26 @@
#include "webfuse/adapter/impl/session_manager.h" #include "webfuse/adapter/impl/session_manager.h"
#include "webfuse/core/util.h" #include "webfuse/core/util.h"
#include "webfuse/core/container_of.h"
#include <stddef.h> #include <stddef.h>
void wf_impl_session_manager_init( void wf_impl_session_manager_init(
struct wf_impl_session_manager * manager) 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( void wf_impl_session_manager_cleanup(
struct wf_impl_session_manager * manager) 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( 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 lws * wsi,
struct wf_impl_authenticators * authenticators, struct wf_impl_authenticators * authenticators,
struct wf_impl_timeout_manager * timeout_manager, 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; struct wf_impl_session * session = wf_impl_session_create(
if (NULL == manager->session.wsi) wsi, authenticators, timeout_manager, server, mount_point);
if (NULL != session)
{ {
session = &manager->session; wf_slist_append(&manager->sessions, &session->item);
wf_impl_session_init(&manager->session, wsi, authenticators, timeout_manager, server);
} }
return session; return session;
@ -36,37 +46,42 @@ struct wf_impl_session * wf_impl_session_manager_get(
struct lws * wsi) struct lws * wsi)
{ {
struct wf_impl_session * session = NULL; 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; 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( void wf_impl_session_manager_remove(
struct wf_impl_session_manager * manager, struct wf_impl_session_manager * manager,
struct lws * wsi) 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); struct wf_slist_item * next = item->next;
manager->session.wsi = NULL; 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/session.h"
#include "webfuse/adapter/impl/fuse_wrapper.h" #include "webfuse/adapter/impl/fuse_wrapper.h"
#include "webfuse/core/slist.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
@ -19,7 +20,7 @@ struct wf_impl_jsonrpc_server;
struct wf_impl_session_manager struct wf_impl_session_manager
{ {
struct wf_impl_session session; struct wf_slist sessions;
}; };
extern void wf_impl_session_manager_init( 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 lws * wsi,
struct wf_impl_authenticators * authenticators, struct wf_impl_authenticators * authenticators,
struct wf_impl_timeout_manager * timeout_manager, 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( extern struct wf_impl_session * wf_impl_session_manager_get(
struct wf_impl_session_manager * manager, struct wf_impl_session_manager * manager,
struct lws * wsi); 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( extern void wf_impl_session_manager_remove(
struct wf_impl_session_manager * manager, struct wf_impl_session_manager * manager,
struct lws * wsi); 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->data = &data[sizeof(struct wf_message) + LWS_PRE];
message->length = length; message->length = length;
message->next = NULL;
json_dumpb(value, message->data, length, JSON_COMPACT); json_dumpb(value, message->data, length, JSON_COMPACT);
} }

View File

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

View File

@ -1,63 +1,17 @@
#include "webfuse/core/message_queue.h" #include "webfuse/core/message_queue.h"
#include "webfuse/core/message.h" #include "webfuse/core/message.h"
#include "webfuse/core/container_of.h"
void wf_message_queue_init(
struct wf_message_queue * queue)
{
queue->first = NULL;
queue->last = NULL;
}
void wf_message_queue_cleanup( void wf_message_queue_cleanup(
struct wf_message_queue * queue) struct wf_slist * queue)
{ {
struct wf_message * message = queue->first; struct wf_slist_item * item = queue->first;
while (NULL != message) 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); wf_message_dispose(message);
message = next; item = next;
} }
wf_message_queue_init(queue); wf_slist_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;
} }

View File

@ -1,39 +1,16 @@
#ifndef WF_MESSAGE_QUEUE_H #ifndef WF_MESSAGE_QUEUE_H
#define 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 #ifdef __cplusplus
extern "C" extern "C"
{ {
#endif #endif
extern void wf_message_queue_init( struct wf_slist;
struct wf_message_queue * queue);
extern void wf_message_queue_cleanup( 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 #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/provider/impl/provider.h"
#include "webfuse/core/util.h" #include "webfuse/core/util.h"
#include "webfuse/core/message.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( static void wfp_impl_client_protocol_respond(
json_t * response, json_t * response,
@ -20,7 +22,7 @@ static void wfp_impl_client_protocol_respond(
struct wf_message * message = wf_message_create(response); struct wf_message * message = wf_message_create(response);
if (NULL != message) if (NULL != message)
{ {
wf_message_queue_push(&protocol->queue, message); wf_slist_append(&protocol->messages, &message->item);
lws_callback_on_writable(protocol->wsi); 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( static int wfp_impl_client_protocol_callback(
struct lws * wsi, struct lws * wsi,
@ -61,6 +83,7 @@ static int wfp_impl_client_protocol_callback(
switch (reason) switch (reason)
{ {
case LWS_CALLBACK_CLIENT_ESTABLISHED: case LWS_CALLBACK_CLIENT_ESTABLISHED:
wfp_impl_client_protocol_add_filesystem(protocol);
protocol->provider.connected(protocol->user_data); protocol->provider.connected(protocol->user_data);
break; break;
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
@ -75,13 +98,14 @@ static int wfp_impl_client_protocol_callback(
case LWS_CALLBACK_SERVER_WRITEABLE: case LWS_CALLBACK_SERVER_WRITEABLE:
// fall-through // fall-through
case LWS_CALLBACK_CLIENT_WRITEABLE: 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); lws_write(wsi, (unsigned char*) message->data, message->length, LWS_WRITE_TEXT);
wf_message_dispose(message); wf_message_dispose(message);
if (!wf_message_queue_empty(&protocol->queue)) if (!wf_slist_empty(&protocol->messages))
{ {
lws_callback_on_writable(wsi); lws_callback_on_writable(wsi);
@ -102,7 +126,7 @@ void wfp_impl_client_protocol_init(
struct wfp_provider const * provider, struct wfp_provider const * provider,
void * user_data) void * user_data)
{ {
wf_message_queue_init(&protocol->queue); wf_slist_init(&protocol->messages);
protocol->wsi = NULL; protocol->wsi = NULL;
@ -116,7 +140,7 @@ void wfp_impl_client_protocol_init(
void wfp_impl_client_protocol_cleanup( void wfp_impl_client_protocol_cleanup(
struct wfp_client_protocol * protocol) 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( 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/provider.h"
#include "webfuse/provider/impl/request.h" #include "webfuse/provider/impl/request.h"
#include "webfuse/core/message_queue.h" #include "webfuse/core/slist.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
@ -20,7 +20,7 @@ struct wfp_client_protocol
struct wfp_provider provider; struct wfp_provider provider;
void * user_data; void * user_data;
struct lws * wsi; struct lws * wsi;
struct wf_message_queue queue; struct wf_slist messages;
}; };
extern void wfp_impl_client_protocol_init( extern void wfp_impl_client_protocol_init(

View File

@ -8,11 +8,11 @@ void wfp_impl_close(
int WF_UNUSED_PARAM(id)) int WF_UNUSED_PARAM(id))
{ {
size_t const param_count = json_array_size(params); 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 * inode_holder = json_array_get(params, 1);
json_t * handle_holder = json_array_get(params, 1); json_t * handle_holder = json_array_get(params, 2);
json_t * flags_holder = json_array_get(params, 2); json_t * flags_holder = json_array_get(params, 3);
if (json_is_integer(inode_holder) && if (json_is_integer(inode_holder) &&
json_is_integer(handle_holder) && json_is_integer(handle_holder) &&

View File

@ -13,9 +13,9 @@ void wfp_impl_getattr(
int id) int id)
{ {
size_t const count = json_array_size(params); 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)) if (json_is_integer(inode_holder))
{ {

View File

@ -12,10 +12,10 @@ void wfp_impl_lookup(
int id) int id)
{ {
size_t const count = json_array_size(params); 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 * inode_holder = json_array_get(params, 1);
json_t * name_holder = json_array_get(params, 1); json_t * name_holder = json_array_get(params, 2);
if (json_is_integer(inode_holder) && if (json_is_integer(inode_holder) &&
json_is_string(name_holder)) json_is_string(name_holder))

View File

@ -9,10 +9,10 @@ void wfp_impl_open(
int id) int id)
{ {
size_t const count = json_array_size(params); 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 * inode_holder = json_array_get(params, 1);
json_t * flags_holder = json_array_get(params, 1); json_t * flags_holder = json_array_get(params, 2);
if (json_is_integer(inode_holder) && if (json_is_integer(inode_holder) &&
json_is_integer(flags_holder)) json_is_integer(flags_holder))

View File

@ -13,12 +13,12 @@ void wfp_impl_read(
int id) int id)
{ {
size_t const count = json_array_size(params); 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 * inode_holder = json_array_get(params, 1);
json_t * handle_holder = json_array_get(params, 1); json_t * handle_holder = json_array_get(params, 2);
json_t * offset_holder = json_array_get(params, 2); json_t * offset_holder = json_array_get(params, 3);
json_t * length_holder = json_array_get(params, 3); json_t * length_holder = json_array_get(params, 4);
if (json_is_integer(inode_holder) && if (json_is_integer(inode_holder) &&
json_is_integer(handle_holder) && json_is_integer(handle_holder) &&

View File

@ -10,9 +10,9 @@ void wfp_impl_readdir(
int id) int id)
{ {
size_t const count = json_array_size(params); 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))) 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);
}