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:
parent
979ff1e689
commit
3a7c064af7
@ -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})
|
||||||
|
58
README.md
58
README.md
@ -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>}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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 |
@ -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
|
BIN
doc/concept.png
BIN
doc/concept.png
Binary file not shown.
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 33 KiB |
@ -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
BIN
doc/filesystem.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
16
doc/filesystem.uml
Normal file
16
doc/filesystem.uml
Normal 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
|
@ -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;
|
||||||
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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(
|
||||||
|
@ -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(
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
13
lib/webfuse/core/container_of.h
Normal file
13
lib/webfuse/core/container_of.h
Normal 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
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
@ -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
77
lib/webfuse/core/slist.c
Normal 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
45
lib/webfuse/core/slist.h
Normal 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
35
lib/webfuse/core/string.c
Normal 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
21
lib/webfuse/core/string.h
Normal 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
|
@ -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(
|
||||||
|
@ -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(
|
||||||
|
@ -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) &&
|
||||||
|
@ -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))
|
||||||
{
|
{
|
||||||
|
@ -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))
|
||||||
|
@ -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))
|
||||||
|
@ -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) &&
|
||||||
|
@ -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
29
test/test_container_of.cc
Normal 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
35
test/test_slist.cc
Normal 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
18
test/test_string.cc
Normal 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);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user