diff --git a/CMakeLists.txt b/CMakeLists.txt index 082d3b0..774eca6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ find_package(PkgConfig REQUIRED) pkg_check_modules(FUSE3 REQUIRED fuse3) pkg_check_modules(LWS REQUIRED libwebsockets) pkg_check_modules(JANSSON REQUIRED jansson) +pkg_check_modules(UUID REQUIRED uuid) add_definitions(-D_FILE_OFFSET_BITS=64) @@ -26,6 +27,7 @@ set(EXTRA_INCLUDE_DIRS ${FUSE3_INCLUDE_DIRS} ${LWS_INCLUDE_DIRS} ${JANSSON_INCLUDE_DIRS} + ${UUID_INCLUDE_DIRS} ) set(EXTRA_LIBS @@ -33,6 +35,7 @@ set(EXTRA_LIBS ${FUSE3_LIBRARIES} ${LWS_LIBRARIES} ${JANSSON_LIBRARIES} + ${UUID_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) @@ -41,6 +44,7 @@ set(EXTRA_CFLAGS ${FUSE3_CFLAGS_OTHER} ${LWS_CFLAGS_OTHER} ${JANSSON_CFLAGS_OTHER} + ${UUID_CFLAGS_OTHER} "-pthread" ) @@ -48,9 +52,11 @@ set(EXTRA_CFLAGS # libwebfuse-core add_library(webfuse-core STATIC + lib/webfuse/core/slist.c lib/webfuse/core/message.c lib/webfuse/core/message_queue.c lib/webfuse/core/status.c + lib/webfuse/core/string.c ) set_target_properties(webfuse-core PROPERTIES OUTPUT_NAME webfuse-core) @@ -272,6 +278,7 @@ pkg_check_modules(GMOCK gmock) add_executable(alltests test/msleep.cc test/mock_authenticator.cc + test/test_container_of.cc test/test_response_parser.cc test/test_server.cc test/test_timepoint.cc @@ -280,6 +287,8 @@ add_executable(alltests test/test_credentials.cc test/test_authenticator.cc test/test_authenticators.cc + test/test_string.cc + test/test_slist.cc ) target_link_libraries(alltests PUBLIC webfuse-adapter-static webfuse-provider-static webfuse-core ${EXTRA_LIBS} ${GMOCK_LIBRARIES} ${GTEST_LIBRARIES}) diff --git a/README.md b/README.md index a9e0ca7..e0b54b2 100644 --- a/README.md +++ b/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. A reference implementation of such a daemon is provided within the examples. The picture above describes the workflow: -- The websocket filesystem daemon (*webfuse daemon*) mounts a filesystem on startup. - It starts the websocket server and waits for incoming connections. +- The websocket filesystem daemon (*webfuse daemon*) waits for incoming connections. -- A remote filesystem provider connects to webfuse daemon via websocket protocol. - The example includes such a provider implemented in HTML and JavaScript. +- A remote filesystem provider connects to webfuse daemon via websocket protocol and adds one or more filesystems. + *Note: the examples include such a provider implemented in HTML and JavaScript.* - Whenever the user makes filesystem requests, such as *ls*, the request is redirected via webfuse daemon to the connected filesystem provider Currently all requests are initiated by webfuse daemon and responded by filesystem provider. This may change in future, e.g. when authentication is supported. +### Filesystem represenation + +![filesystem](doc/filesystem.png) + +To handle multiple filesystems, that are registered by one or more providers, webfuse daemon maintains a directory structure as shown above. + +- **mount_point** is the entry point of the drectory structure + +- **fwupdate** is a name defined by the provider when filesystem was registered + *Note: the picture above shows two providers, where both registered a filesystem named "fwupdate"* + +- **<uuid>** is the filesystem id choosen by webfuse daemon to distinguish different filesystems + +- **default** is a symbolic link maintained by webfuse daemon to identify the default filesystem + +This directoy structure allows to handle multiple filesystems registered by multiple providers. +It can be used as a kind of service registry, where each filesystem represents a service. +The named subdirectores distinguish differend service types. The symbolic link *default* can be used to identify the +default service and the listing of a named subdirectory can be used to list available services of a particular type. + ## Similar Projects ### Davfs2 @@ -141,7 +160,7 @@ Notfications are used to inform a receiver about something. Unlike requests, not Retrieve information about a filesystem entry by name. - webfuse daemon: {"method": "lookup", "params": [, ], "id": } + webfuse daemon: {"method": "lookup", "params": [, , ], "id": } fs provider: {"result": { "inode": , "mode" : , @@ -154,6 +173,7 @@ Retrieve information about a filesystem entry by name. | Item | Data type | Description | | ----------- | --------------- | ------------------------------------------- | +| filesystem | string | name of the filesystem | | parent | integer | inode of parent directory (1 = root) | | name | string | name of the filesystem object to look up | | inode | integer | inode of the filesystem object | @@ -168,7 +188,7 @@ Retrieve information about a filesystem entry by name. Get file attributes. - webfuse daemon: {"method": "getattr", "params": [], "id": } + webfuse daemon: {"method": "getattr", "params": [, ], "id": } fs provider: {"result": { "mode" : , "type" : , @@ -180,6 +200,7 @@ Get file attributes. | Item | Data type | Description | | ----------- | --------------- | ------------------------------------------- | +| filesystem | string | name of the filesystem | | inode | integer | inode of the filesystem object | | mode | integer | unix file mode | | type | "file" or "dir" | type of filesystem object | @@ -194,7 +215,7 @@ Read directory contents. Result is an array of name-inode pairs for each entry. The generic entries "." and ".." should also be provided. - webfuse daemon: {"method": "readdir", "params": [], "id": } + webfuse daemon: {"method": "readdir", "params": [, ], "id": } fs provider: {"result": [ {"name": , "inode": }, ... @@ -202,6 +223,7 @@ Result is an array of name-inode pairs for each entry. The generic entries | Item | Data type | Description | | ----------- | --------------- | ------------------------------ | +| filesystem | string | name of the filesystem | | dir_inode | integer | inode of the directory to read | | name | integer | name of the entry | | inode | integer | inode of the entry | @@ -210,11 +232,12 @@ Result is an array of name-inode pairs for each entry. The generic entries Open a file. - webfuse daemon: {"method": "readdir", "params": [, ], "id": } + webfuse daemon: {"method": "readdir", "params": [, , ], "id": } fs provider: {"result": {"handle": }, "id": } | Item | Data type | Description | | ----------- | ----------| ----------------------------- | +| filesystem | string | name of the filesystem | | inode | integer | inode of the file | | flags | integer | access mode flags (see below) | | handle | integer | handle of the file | @@ -237,10 +260,11 @@ Open a file. Informs filesystem provider, that a file is closed. Since `close` is a notification, it cannot fail. - webfuse daemon: {"method": "close", "params": [, , ], "id": } + webfuse daemon: {"method": "close", "params": [, , , ], "id": } | Item | Data type | Description | | ----------- | ----------| ---------------------------- | +| filesystem | string | name of the filesystem | | inode | integer | inode of the file | | handle | integer | handle of the file | | flags | integer | access mode flags (see open) | @@ -249,7 +273,7 @@ Since `close` is a notification, it cannot fail. Read from an open file. - webfuse daemon: {"method": "close", "params": [, , , ], "id": } + webfuse daemon: {"method": "close", "params": [, , , , ], "id": } fs provider: {"result": { "data": , "format": , @@ -258,6 +282,7 @@ Read from an open file. | Item | Data type | Description | | ----------- | ----------| ----------------------------- | +| filesystem | string | name of the filesystem | | inode | integer | inode of the file | | handle | integer | handle of the file | | offset | integer | Offet to start read operation | @@ -275,10 +300,21 @@ Read from an open file. ### Requests (Provider -> Adapter) +#### add_filesystem + +Adds a filesystem. + + fs provider: {"method": "add_filesytem", "params": [], "id": } + webfuse daemon: {"result": {"id": }, "id": } + +| Item | Data type | Description | +| ----------- | ----------| ------------------------------- | +| name | string | name and id of filesystem | + #### authtenticate Authenticate the provider. -If authentication is enabled, a provider must be authenticated by the adapter before the adapter will send any messages. +If authentication is enabled, a provider must be authenticated by the adapter before filesystems can be added. fs provider: {"method": "authenticate", "params": [, ], "id": } webfuse daemon: {"result": {}, "id": } diff --git a/build/amd64-ubuntu-builder.dockerfile b/build/amd64-ubuntu-builder.dockerfile index d81fe3f..c28d356 100644 --- a/build/amd64-ubuntu-builder.dockerfile +++ b/build/amd64-ubuntu-builder.dockerfile @@ -14,7 +14,8 @@ RUN set -x \ rsync \ gdb \ gdbserver \ - valgrind + valgrind \ + uuid-dev COPY src /usr/local/src diff --git a/build/arm32v7-ubuntu-builder.dockerfile b/build/arm32v7-ubuntu-builder.dockerfile index a2e1cd7..5c6ad41 100644 --- a/build/arm32v7-ubuntu-builder.dockerfile +++ b/build/arm32v7-ubuntu-builder.dockerfile @@ -17,7 +17,8 @@ RUN set -x \ pkg-config \ rsync \ gdb \ - gdbserver + gdbserver \ + uuid-dev COPY src /usr/local/src diff --git a/doc/authenticate.png b/doc/authenticate.png index b0df17d..b994033 100644 Binary files a/doc/authenticate.png and b/doc/authenticate.png differ diff --git a/doc/authenticate.uml b/doc/authenticate.uml index 00a5f52..263ac5b 100644 --- a/doc/authenticate.uml +++ b/doc/authenticate.uml @@ -1,12 +1,10 @@ @startuml participant "Filesystem Provider\n(e.g. Webbrowser)" as provider participant "webfuse\ndaemon" as daemon -actor "user" as user -group directory listing fails without authentication -user -> daemon : ls -daemon -> daemon : is_authenticated -daemon -->x user : error: no entry +group add filesystem fails without authentication +provider -> daemon : add_filesystem +daemon -->x provider : error: access denied end @@ -17,12 +15,10 @@ daemon -> daemon: check(credentials) daemon --> provider: result end -group directory listing succeeds after authentication -user -> daemon : ls -daemon -> daemon : is_authenticated -daemon -> provider : readdir -provider --> daemon : readdir_resp -daemon --> user : [., ..] +group add filesystem succeeds after authentication + provider -> daemon: add_filesystem + daemon -> daemon : fuse_mount + daemon -> provider: okay end @enduml \ No newline at end of file diff --git a/doc/concept.png b/doc/concept.png index 9ab4e90..64e3475 100644 Binary files a/doc/concept.png and b/doc/concept.png differ diff --git a/doc/concept.uml b/doc/concept.uml index a7ec266..f5d4cb0 100644 --- a/doc/concept.uml +++ b/doc/concept.uml @@ -4,7 +4,6 @@ participant "webfuse\ndaemon" as daemon actor "user" as user group startup -daemon -> daemon : fuse_mount daemon -> daemon : start ws end ... @@ -16,9 +15,15 @@ provider -> daemon : connect daemon -> daemon: check credentials daemon --> provider: okay end + + provider -> daemon: add_filesystem + daemon -> daemon : fuse_mount + daemon -> provider: okay + end ... + group directory listing user -> daemon : ls daemon -> provider : readdir diff --git a/doc/filesystem.png b/doc/filesystem.png new file mode 100644 index 0000000..4b4106a Binary files /dev/null and b/doc/filesystem.png differ diff --git a/doc/filesystem.uml b/doc/filesystem.uml new file mode 100644 index 0000000..1b78837 --- /dev/null +++ b/doc/filesystem.uml @@ -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 \ No newline at end of file diff --git a/example/daemon/main.c b/example/daemon/main.c index 1bafb8c..de6ac51 100644 --- a/example/daemon/main.c +++ b/example/daemon/main.c @@ -50,13 +50,14 @@ static bool authenticate(struct wf_credentials * creds, void * user_data) char const * password = wf_credentials_get(creds, "password"); if ((NULL != username) && (NULL != password)) { - struct userdb * db = userdb_create(""); + struct userdb * db = userdb_create(""); result = userdb_load(db, args->passwd_path); if (result) { result = userdb_check(db, username, password); - userdb_dispose(db); } + + userdb_dispose(db); } return result; diff --git a/example/daemon/www/js/connection_view.js b/example/daemon/www/js/connection_view.js index c46b584..31576ae 100644 --- a/example/daemon/www/js/connection_view.js +++ b/example/daemon/www/js/connection_view.js @@ -1,5 +1,6 @@ export class ConnectionView { - constructor(client) { + constructor(client, provider) { + this._provider = provider; this._client = client; this._client.onopen = () => { this._onConnectionOpened(); }; this._client.onclose = () => { this._onConnectionClosed(); }; @@ -28,6 +29,14 @@ export class ConnectionView { const authenticateBox = document.createElement("div"); this.element.appendChild(authenticateBox); + const authLabel = document.createElement("span"); + authLabel.textContent = "use authentication:"; + authenticateBox.appendChild(authLabel); + + this.authenticateCheckbox = document.createElement("input"); + this.authenticateCheckbox.type = "checkbox"; + authenticateBox.appendChild(this.authenticateCheckbox); + const usernameLabel = document.createElement("span"); usernameLabel.textContent = "user:"; authenticateBox.appendChild(usernameLabel); @@ -45,12 +54,6 @@ export class ConnectionView { this.passwordTextbox.type = "password"; this.passwordTextbox.value = "secret"; authenticateBox.appendChild(this.passwordTextbox); - - this.authenticateButton = document.createElement("input"); - this.authenticateButton.type = "button"; - this.authenticateButton.value = "authenticate"; - this.authenticateButton.addEventListener("click", () => { this._onAuthenticateButtonClicked(); }); - authenticateBox.appendChild(this.authenticateButton); } _onConnectButtonClicked() { @@ -65,14 +68,21 @@ export class ConnectionView { _onAuthenticateButtonClicked() { if (this._client.isConnected()) { - const username = this.usernameTextbox.value; - const password = this.passwordTextbox.value; - this._client.authenticate("username", { username, password }); } } _onConnectionOpened() { + if (this.authenticateCheckbox.checked) { + const username = this.usernameTextbox.value; + const password = this.passwordTextbox.value; + + const promise = this._client.authenticate("username", { username, password }); + promise.then(() => { this._client.addProvider("test", this._provider); }); + } else { + this._client.addProvider("test", this._provider); + } + this.connectButton.value = "disconnect"; } diff --git a/example/daemon/www/js/startup.js b/example/daemon/www/js/startup.js index cdc0796..284d229 100644 --- a/example/daemon/www/js/startup.js +++ b/example/daemon/www/js/startup.js @@ -17,8 +17,8 @@ function startup() { "say_hello.sh": { inode: 3, mode: mode("0555"), type: "file", contents: "#!/bin/sh\necho hello\n"} } }); - const client = new Client(provider); - const connectionView = new ConnectionView(client); + const client = new Client(); + const connectionView = new ConnectionView(client, provider); document.getElementById('connection').appendChild(connectionView.element); } diff --git a/example/daemon/www/js/webfuse/client.js b/example/daemon/www/js/webfuse/client.js index 2838732..65d24f2 100644 --- a/example/daemon/www/js/webfuse/client.js +++ b/example/daemon/www/js/webfuse/client.js @@ -4,7 +4,9 @@ export class Client { static get _PROTOCOL() { return "fs"; } constructor(provider) { - this._provider = provider; + this._provider = { }; + this._pendingRequests = {}; + this._id = 0; this._ws = null; this.onopen = () => { }; this.onclose = () => { }; @@ -24,11 +26,27 @@ export class Client { }; } + _invokeRequest(method, params) { + this._id += 1; + const id = this._id; + const request = {method, params, id}; + + return new Promise((resolve, reject) => { + this._pendingRequests[id] = {resolve, reject}; + this._ws.send(JSON.stringify(request)); + }); + } + authenticate(type, credentials) { + return this._invokeRequest("authenticate", [type, credentials]); + } + + addProvider(name, provider) { + this._provider[name] = provider; const request = { - "method": "authenticate", - "params": [type, credentials], - "id": 42 + "method": "add_filesystem", + "params": [name], + "id": 23 }; this._ws.send(JSON.stringify(request)); @@ -45,27 +63,61 @@ export class Client { return ((this._ws) && (this._ws.readyState === WebSocket.OPEN)); } + _isRequest(request) { + const method = request.method; + + return (("string" === typeof(method)) && ("params" in request)); + } + + _isResponse(response) { + const id = response.id; + + return (("number" === typeof(id)) && (("result" in response) || ("error" in response))); + } + + _removePendingRequest(id) { + let result = null; + + if (id in this._pendingRequests) { + result = this._pendingRequests[id]; + Reflect.deleteProperty(this._pendingRequests, id); + } + + return result; + } + _onmessage(message) { try { - const request = JSON.parse(message.data); - const method = request.method; - const id = request.id; - const params = request.params; + const data = JSON.parse(message.data); - if ("string" !== typeof(method)) { - throw new Error("parse error: missing field: \"method\""); + if (this._isRequest(data)) { + const method = data.method; + const id = data.id; + const params = data.params; + + if ("number" === typeof(id)) { + this._invoke(method, params, id); + } + else { + this._notify(method, params); + } } - - if (!params) { - throw new Error("parse error: missing field: \"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 ("number" === typeof(request.id)) { - this._invoke(method, params, id); - } - else { - this._notify(method, params); - } } catch (ex) { // swallow @@ -114,28 +166,48 @@ export class Client { } } - async _lookup([parent, name]) { - return this._provider.lookup(parent, name); + _getProvider(name) { + if (name in this._provider) { + return this._provider[name]; + } + else { + throw new Error('Unknown provider'); + } } - async _getattr([inode]) { - return this._provider.getattr(inode); + async _lookup([providerName, parent, name]) { + const provider = this._getProvider(providerName); + + return provider.lookup(parent, name); } - async _readdir([inode]) { - return this._provider.readdir(inode); + async _getattr([providerName, inode]) { + const provider = this._getProvider(providerName); + + return provider.getattr(inode); } - async _open([inode, mode]) { - return this._provider.open(inode, mode); + async _readdir([providerName, inode]) { + const provider = this._getProvider(providerName); + + return provider.readdir(inode); } - _close([inode, handle, mode]) { - this._provider.close(inode, handle, mode); + async _open([providerName, inode, mode]) { + const provider = this._getProvider(providerName); + + return provider.open(inode, mode); + } + + _close([providerName, inode, handle, mode]) { + const provider = this._getProvider(providerName); + + provider.close(inode, handle, mode); } - async _read([inode, handle, offset, length]) { - const data = await this._provider.read(inode, handle, offset, length); + async _read([providerName, inode, handle, offset, length]) { + const provider = this._getProvider(providerName); + const data = await provider.read(inode, handle, offset, length); if ("string" === typeof(data)) { return { diff --git a/example/provider/main.c b/example/provider/main.c index 33290cd..1cb27cf 100644 --- a/example/provider/main.c +++ b/example/provider/main.c @@ -340,7 +340,7 @@ int main(int argc, char* argv[]) .parent = 1, .inode = 2, .name = "hello.txt", - .mode = 0555, + .mode = 0444, .type = FS_FILE, .content="hello, world!", .content_length = 13, diff --git a/lib/webfuse/adapter/impl/filesystem.c b/lib/webfuse/adapter/impl/filesystem.c index 0e52b9f..3cfcca7 100644 --- a/lib/webfuse/adapter/impl/filesystem.c +++ b/lib/webfuse/adapter/impl/filesystem.c @@ -1,5 +1,16 @@ #include "webfuse/adapter/impl/filesystem.h" #include "webfuse/adapter/impl/operations.h" +#include "webfuse/adapter/impl/session.h" + +#include "webfuse/core/string.h" + +#include +#include + +#include +#include +#include +#include #include #include @@ -16,23 +27,118 @@ static struct fuse_lowlevel_ops const filesystem_operations = .read = &wf_impl_operation_read }; +static char * wf_impl_filesystem_create_id(void) +{ + uuid_t uuid; + uuid_generate(uuid); + char id[UUID_STR_LEN]; + uuid_unparse(uuid, id); -bool wf_impl_filesystem_init( - struct wf_impl_filesystem * filesystem, - struct wf_impl_session_manager * session_manager, - char * mount_point) + return strdup(id); +} + +static bool wf_impl_filesystem_is_link_broken(char const * path, char const * id) { bool result = false; + char buffer[UUID_STR_LEN]; + ssize_t count = readlink(path, buffer, UUID_STR_LEN); + if ((0 < count) && (count < UUID_STR_LEN)) + { + buffer[count] = '\0'; + result = (0 == strcmp(buffer, id)); + } + + return result; +} + +static bool wf_impl_filesystem_link_first_subdir( + char const * link_path, + char const * path) +{ + bool result = false; + DIR * dir = opendir(path); + if (NULL != dir) + { + struct dirent * entry = readdir(dir); + while (NULL != entry) + { + if ((DT_DIR == entry->d_type) && ('.' != entry->d_name[0])) + { + symlink(entry->d_name, link_path); + result = true; + break; + } + + entry = readdir(dir); + } + + closedir(dir); + } + + return result; +} + +static void wf_impl_filesystem_cleanup( + struct wf_impl_filesystem * filesystem) +{ + fuse_session_reset(filesystem->session); + fuse_session_unmount(filesystem->session); + fuse_session_destroy(filesystem->session); + filesystem->session = NULL; + + free(filesystem->buffer.mem); + fuse_opt_free_args(&filesystem->args); + + rmdir(filesystem->root_path); + + if (wf_impl_filesystem_is_link_broken(filesystem->default_path, filesystem->id)) + { + unlink(filesystem->default_path); + + bool const success = wf_impl_filesystem_link_first_subdir(filesystem->default_path, filesystem->service_path); + if (!success) + { + rmdir(filesystem->service_path); + } + } + + + free(filesystem->user_data.name); + free(filesystem->id); + free(filesystem->root_path); + free(filesystem->default_path); + free(filesystem->service_path); +} + + +static bool wf_impl_filesystem_init( + struct wf_impl_filesystem * filesystem, + struct wf_impl_session * session, + char const * name) +{ + bool result = false; + char * argv[] = {"", NULL}; filesystem->args.argc = 1; filesystem->args.argv = argv; filesystem->args.allocated = 0; - filesystem->user_data.session_manager = session_manager; + filesystem->user_data.session = session; filesystem->user_data.timeout = 1.0; + filesystem->user_data.name = strdup(name); memset(&filesystem->buffer, 0, sizeof(struct fuse_buf)); + filesystem->service_path = wf_create_string("%s/%s", session->mount_point, name); + mkdir(filesystem->service_path, 0755); + + filesystem->id = wf_impl_filesystem_create_id(); + filesystem->root_path = wf_create_string("%s/%s/%s", session->mount_point, name, filesystem->id); + mkdir(filesystem->root_path, 0755); + + filesystem->default_path = wf_create_string("%s/%s/default", session->mount_point, name); + symlink(filesystem->id, filesystem->default_path); + filesystem->session = fuse_session_new( &filesystem->args, &filesystem_operations, @@ -40,31 +146,50 @@ bool wf_impl_filesystem_init( &filesystem->user_data); if (NULL != filesystem->session) { - result = (0 == fuse_session_mount(filesystem->session, mount_point)); + result = (0 == fuse_session_mount(filesystem->session, filesystem->root_path)); + } + + if (result) + { + lws_sock_file_fd_type fd; + fd.filefd = fuse_session_fd(filesystem->session); + struct lws_protocols const * protocol = lws_get_protocol(session->wsi); + filesystem->wsi = lws_adopt_descriptor_vhost(lws_get_vhost(session->wsi), LWS_ADOPT_RAW_FILE_DESC, fd, protocol->name, session->wsi); + + if (NULL == filesystem->wsi) + { + wf_impl_filesystem_cleanup(filesystem); + result = false; + } + } return result; } -void wf_impl_filesystem_cleanup( - struct wf_impl_filesystem * filesystem) +struct wf_impl_filesystem * wf_impl_filesystem_create( + struct wf_impl_session * session, + char const * name) { - if (NULL != filesystem->session) + struct wf_impl_filesystem * filesystem = malloc(sizeof(struct wf_impl_filesystem)); + if (NULL != filesystem) { - fuse_session_reset(filesystem->session); - fuse_session_unmount(filesystem->session); - fuse_session_destroy(filesystem->session); - filesystem->session = NULL; + bool success = wf_impl_filesystem_init(filesystem, session, name); + if (!success) + { + free(filesystem); + filesystem = NULL; + } } - free(filesystem->buffer.mem); - fuse_opt_free_args(&filesystem->args); + return filesystem; } -int wf_impl_filesystem_get_fd( +void wf_impl_filesystem_dispose( struct wf_impl_filesystem * filesystem) { - return fuse_session_fd(filesystem->session); + wf_impl_filesystem_cleanup(filesystem); + free(filesystem); } void wf_impl_filesystem_process_request( diff --git a/lib/webfuse/adapter/impl/filesystem.h b/lib/webfuse/adapter/impl/filesystem.h index af07c32..648dfbd 100644 --- a/lib/webfuse/adapter/impl/filesystem.h +++ b/lib/webfuse/adapter/impl/filesystem.h @@ -7,31 +7,36 @@ #include "webfuse/adapter/impl/fuse_wrapper.h" #include "webfuse/adapter/impl/operations.h" +#include "webfuse/core/slist.h" #ifdef __cplusplus extern "C" { #endif -struct wf_impl_session_manager; +struct wf_impl_session; +struct lws; struct wf_impl_filesystem { + struct wf_slist_item item; struct fuse_args args; struct fuse_session * session; struct fuse_buf buffer; struct wf_impl_operations_context user_data; + struct lws * wsi; + char * name; + char * id; + char * service_path; + char * default_path; + char * root_path; }; -extern bool wf_impl_filesystem_init( - struct wf_impl_filesystem * filesystem, - struct wf_impl_session_manager * session_manager, - char * mount_point); +extern struct wf_impl_filesystem * wf_impl_filesystem_create( + struct wf_impl_session * session, + char const * name); -extern void wf_impl_filesystem_cleanup( - struct wf_impl_filesystem * filesystem); - -extern int wf_impl_filesystem_get_fd( +extern void wf_impl_filesystem_dispose( struct wf_impl_filesystem * filesystem); extern void wf_impl_filesystem_process_request( diff --git a/lib/webfuse/adapter/impl/operation/close.c b/lib/webfuse/adapter/impl/operation/close.c index 6ccefc8..ca63a8e 100644 --- a/lib/webfuse/adapter/impl/operation/close.c +++ b/lib/webfuse/adapter/impl/operation/close.c @@ -13,12 +13,12 @@ void wf_impl_operation_close( struct fuse_file_info * file_info) { struct wf_impl_operations_context * user_data = fuse_req_userdata(request); - struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, inode); + struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data); if (NULL != rpc) { int handle = (int) (file_info->fh & INT_MAX); - wf_impl_jsonrpc_proxy_notify(rpc, "close", "iii", inode, handle, file_info->flags); + wf_impl_jsonrpc_proxy_notify(rpc, "close", "siii", user_data->name, inode, handle, file_info->flags); } fuse_reply_err(request, 0); diff --git a/lib/webfuse/adapter/impl/operation/getattr.c b/lib/webfuse/adapter/impl/operation/getattr.c index 98993e8..75736a2 100644 --- a/lib/webfuse/adapter/impl/operation/getattr.c +++ b/lib/webfuse/adapter/impl/operation/getattr.c @@ -81,7 +81,7 @@ void wf_impl_operation_getattr ( { struct fuse_ctx const * context = fuse_req_ctx(request); struct wf_impl_operations_context * user_data = fuse_req_userdata(request); - struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, inode); + struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data); if (NULL != rpc) { @@ -91,7 +91,7 @@ void wf_impl_operation_getattr ( getattr_context->gid = context->gid; getattr_context->timeout = user_data->timeout; - wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_getattr_finished, getattr_context, "getattr", "i", inode); + wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_getattr_finished, getattr_context, "getattr", "si", user_data->name, inode); } else { diff --git a/lib/webfuse/adapter/impl/operation/lookup.c b/lib/webfuse/adapter/impl/operation/lookup.c index 840a754..3749123 100644 --- a/lib/webfuse/adapter/impl/operation/lookup.c +++ b/lib/webfuse/adapter/impl/operation/lookup.c @@ -90,7 +90,7 @@ void wf_impl_operation_lookup ( { struct fuse_ctx const * context = fuse_req_ctx(request); struct wf_impl_operations_context * user_data = fuse_req_userdata(request); - struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, parent); + struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data); if (NULL != rpc) { @@ -100,7 +100,7 @@ void wf_impl_operation_lookup ( lookup_context->gid = context->gid; lookup_context->timeout = user_data->timeout; - wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_lookup_finished, lookup_context, "lookup", "is", (int) (parent & INT_MAX), name); + wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_lookup_finished, lookup_context, "lookup", "sis", user_data->name, (int) (parent & INT_MAX), name); } else { diff --git a/lib/webfuse/adapter/impl/operation/open.c b/lib/webfuse/adapter/impl/operation/open.c index 51894fa..e82b865 100644 --- a/lib/webfuse/adapter/impl/operation/open.c +++ b/lib/webfuse/adapter/impl/operation/open.c @@ -47,11 +47,11 @@ void wf_impl_operation_open( struct fuse_file_info * file_info) { struct wf_impl_operations_context * user_data = fuse_req_userdata(request); - struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, inode); + struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data); if (NULL != rpc) { - wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_open_finished, request, "open", "ii", inode, file_info->flags); + wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_open_finished, request, "open", "sii", user_data->name, inode, file_info->flags); } else { diff --git a/lib/webfuse/adapter/impl/operation/read.c b/lib/webfuse/adapter/impl/operation/read.c index 8c956fb..ea89eff 100644 --- a/lib/webfuse/adapter/impl/operation/read.c +++ b/lib/webfuse/adapter/impl/operation/read.c @@ -87,13 +87,13 @@ void wf_impl_operation_read( struct fuse_file_info * file_info) { struct wf_impl_operations_context * user_data = fuse_req_userdata(request); - struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, inode); + struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data); if (NULL != rpc) { int const length = (size <= WF_MAX_READ_LENGTH) ? (int) size : WF_MAX_READ_LENGTH; int handle = (file_info->fh & INT_MAX); - wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_read_finished, request, "read", "iiii", inode, handle, (int) offset, length); + wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_read_finished, request, "read", "siiii", user_data->name, inode, handle, (int) offset, length); } else { diff --git a/lib/webfuse/adapter/impl/operation/readdir.c b/lib/webfuse/adapter/impl/operation/readdir.c index 104e807..85cfd5b 100644 --- a/lib/webfuse/adapter/impl/operation/readdir.c +++ b/lib/webfuse/adapter/impl/operation/readdir.c @@ -137,7 +137,7 @@ void wf_impl_operation_readdir ( struct fuse_file_info * WF_UNUSED_PARAM(file_info)) { struct wf_impl_operations_context * user_data = fuse_req_userdata(request); - struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, inode); + struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data); if (NULL != rpc) { @@ -146,7 +146,7 @@ void wf_impl_operation_readdir ( readdir_context->size = size; readdir_context->offset = offset; - wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_readdir_finished, readdir_context, "readdir", "i", inode); + wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_readdir_finished, readdir_context, "readdir", "si", user_data->name, inode); } else { diff --git a/lib/webfuse/adapter/impl/operations.c b/lib/webfuse/adapter/impl/operations.c index 75949b5..393ab99 100644 --- a/lib/webfuse/adapter/impl/operations.c +++ b/lib/webfuse/adapter/impl/operations.c @@ -4,13 +4,11 @@ #include struct wf_impl_jsonrpc_proxy * wf_impl_operations_context_get_proxy( - struct wf_impl_operations_context * context, - fuse_ino_t inode) + struct wf_impl_operations_context * context) { struct wf_impl_jsonrpc_proxy * proxy = NULL; - struct wf_impl_session_manager * session_manger = context->session_manager; - struct wf_impl_session * session = wf_impl_session_manager_get_by_inode(session_manger, inode); + struct wf_impl_session * session = context->session; if (NULL != session) { proxy = &session->rpc; diff --git a/lib/webfuse/adapter/impl/operations.h b/lib/webfuse/adapter/impl/operations.h index fe1a540..debe4fa 100644 --- a/lib/webfuse/adapter/impl/operations.h +++ b/lib/webfuse/adapter/impl/operations.h @@ -7,13 +7,14 @@ extern "C" { #endif -struct wf_impl_session_manager; +struct wf_impl_session; struct wf_impl_jsonrpc_proxy; struct wf_impl_operations_context { - struct wf_impl_session_manager * session_manager; + struct wf_impl_session * session; double timeout; + char * name; }; extern void wf_impl_operation_lookup ( @@ -49,8 +50,7 @@ extern void wf_impl_operation_read( struct fuse_file_info *fi); extern struct wf_impl_jsonrpc_proxy * wf_impl_operations_context_get_proxy( - struct wf_impl_operations_context * context, - fuse_ino_t inode); + struct wf_impl_operations_context * context); #ifdef __cplusplus } diff --git a/lib/webfuse/adapter/impl/server.c b/lib/webfuse/adapter/impl/server.c index 5bddb10..13250f6 100644 --- a/lib/webfuse/adapter/impl/server.c +++ b/lib/webfuse/adapter/impl/server.c @@ -108,18 +108,11 @@ struct wf_server * wf_impl_server_create( server = malloc(sizeof(struct wf_server)); if (NULL != server) { - if (wf_impl_server_protocol_init(&server->protocol, config->mount_point)) - { - server->shutdown_requested = false; - wf_impl_server_config_clone(config, &server->config); - wf_impl_authenticators_move(&server->config.authenticators, &server->protocol.authenticators); - server->context = wf_impl_server_context_create(server); - } - else - { - free(server); - server = NULL; - } + wf_impl_server_protocol_init(&server->protocol, config->mount_point); + server->shutdown_requested = false; + wf_impl_server_config_clone(config, &server->config); + wf_impl_authenticators_move(&server->config.authenticators, &server->protocol.authenticators); + server->context = wf_impl_server_context_create(server); } } diff --git a/lib/webfuse/adapter/impl/server_protocol.c b/lib/webfuse/adapter/impl/server_protocol.c index 0e89437..27a6fd8 100644 --- a/lib/webfuse/adapter/impl/server_protocol.c +++ b/lib/webfuse/adapter/impl/server_protocol.c @@ -1,12 +1,12 @@ #include "webfuse/adapter/impl/server_protocol.h" #include +#include #include #include "webfuse/core/message.h" #include "webfuse/core/util.h" -#include "webfuse/adapter/impl/filesystem.h" #include "webfuse/adapter/impl/credentials.h" #include "webfuse/adapter/impl/jsonrpc/request.h" @@ -25,23 +25,14 @@ static int wf_impl_server_protocol_callback( switch (reason) { - case LWS_CALLBACK_PROTOCOL_INIT: - { - lws_sock_file_fd_type fd; - fd.filefd = wf_impl_filesystem_get_fd(&protocol->filesystem); - if (!lws_adopt_descriptor_vhost(lws_get_vhost(wsi), LWS_ADOPT_RAW_FILE_DESC, fd, ws_protocol->name, NULL)) - { - fprintf(stderr, "error: unable to adopt fd"); - } - } - break; case LWS_CALLBACK_ESTABLISHED: session = wf_impl_session_manager_add( &protocol->session_manager, wsi, &protocol->authenticators, &protocol->timeout_manager, - &protocol->server); + &protocol->server, + protocol->mount_point); if (NULL != session) { @@ -64,7 +55,10 @@ static int wf_impl_server_protocol_callback( } break; case LWS_CALLBACK_RAW_RX_FILE: - wf_impl_filesystem_process_request(&protocol->filesystem); + if (NULL != session) + { + wf_impl_session_process_filesystem_request(session, wsi); + } break; default: break; @@ -79,11 +73,7 @@ struct wf_server_protocol * wf_impl_server_protocol_create( struct wf_server_protocol * protocol = malloc(sizeof(struct wf_server_protocol)); if (NULL != protocol) { - if (!wf_impl_server_protocol_init(protocol, mount_point)) - { - free(protocol); - protocol = NULL; - } + wf_impl_server_protocol_init(protocol, mount_point); } return protocol; @@ -140,35 +130,91 @@ static void wf_impl_server_protocol_authenticate( } } -bool wf_impl_server_protocol_init( +static bool wf_impl_server_protocol_check_name(char const * value) +{ + while ('\0' != *value) + { + char const c = * value; + if (!isalpha(c) && !isdigit(c) && ('_' != c)) + { + return false; + } + value++; + } + + return true; +} + +static void wf_impl_server_protocol_add_filesystem( + struct wf_impl_jsonrpc_request * request, + char const * WF_UNUSED_PARAM(method_name), + json_t * params, + void * WF_UNUSED_PARAM(user_data)) +{ + struct wf_impl_session * session = wf_impl_jsonrpc_request_get_userdata(request); + wf_status status = (session->is_authenticated) ? WF_GOOD : WF_BAD_ACCESS_DENIED; + + char const * name = NULL; + if (WF_GOOD == status) + { + json_t * name_holder = json_array_get(params, 0); + if (json_is_string(name_holder)) + { + name = json_string_value(name_holder); + if (wf_impl_server_protocol_check_name(name)) + { + bool const success = wf_impl_session_add_filesystem(session, name); + if (!success) + { + status = WF_BAD; + } + } + else + { + status = WF_BAD_FORMAT; + } + } + else + { + status = WF_BAD_FORMAT; + } + + } + + if (WF_GOOD == status) + { + json_t * result = json_object(); + json_object_set_new(result, "id", json_string(name)); + wf_impl_jsonrpc_respond(request, result); + } + else + { + wf_impl_jsonrpc_respond_error(request, status); + } + + +} + + +void wf_impl_server_protocol_init( struct wf_server_protocol * protocol, char * mount_point) { + protocol->mount_point = strdup(mount_point); + wf_impl_timeout_manager_init(&protocol->timeout_manager); wf_impl_session_manager_init(&protocol->session_manager); wf_impl_authenticators_init(&protocol->authenticators); wf_impl_jsonrpc_server_init(&protocol->server); wf_impl_jsonrpc_server_add(&protocol->server, "authenticate", &wf_impl_server_protocol_authenticate, protocol); - - bool const success = wf_impl_filesystem_init(&protocol->filesystem, &protocol->session_manager, mount_point); - - // cleanup on error - if (!success) - { - wf_impl_jsonrpc_server_cleanup(&protocol->server); - wf_impl_authenticators_cleanup(&protocol->authenticators); - wf_impl_timeout_manager_cleanup(&protocol->timeout_manager); - wf_impl_session_manager_cleanup(&protocol->session_manager); - } - - return success; + wf_impl_jsonrpc_server_add(&protocol->server, "add_filesystem", &wf_impl_server_protocol_add_filesystem, protocol); } void wf_impl_server_protocol_cleanup( struct wf_server_protocol * protocol) { - wf_impl_filesystem_cleanup(&protocol->filesystem); + free(protocol->mount_point); wf_impl_jsonrpc_server_cleanup(&protocol->server); wf_impl_timeout_manager_cleanup(&protocol->timeout_manager); wf_impl_authenticators_cleanup(&protocol->authenticators); diff --git a/lib/webfuse/adapter/impl/server_protocol.h b/lib/webfuse/adapter/impl/server_protocol.h index 8a4b0d7..2ae0fe6 100644 --- a/lib/webfuse/adapter/impl/server_protocol.h +++ b/lib/webfuse/adapter/impl/server_protocol.h @@ -1,7 +1,6 @@ #ifndef WF_ADAPTER_IMPL_SERVER_PROTOCOL_H #define WF_ADAPTER_IMPL_SERVER_PROTOCOL_H -#include "webfuse/adapter/impl/filesystem.h" #include "webfuse/adapter/impl/jsonrpc/proxy.h" #include "webfuse/adapter/impl/time/timeout_manager.h" #include "webfuse/adapter/impl/authenticators.h" @@ -17,14 +16,14 @@ struct lws_protocols; struct wf_server_protocol { + char * mount_point; struct wf_impl_timeout_manager timeout_manager; - struct wf_impl_filesystem filesystem; struct wf_impl_authenticators authenticators; struct wf_impl_session_manager session_manager; struct wf_impl_jsonrpc_server server; }; -extern bool wf_impl_server_protocol_init( +extern void wf_impl_server_protocol_init( struct wf_server_protocol * protocol, char * mount_point); diff --git a/lib/webfuse/adapter/impl/session.c b/lib/webfuse/adapter/impl/session.c index b45027d..ae67cc2 100644 --- a/lib/webfuse/adapter/impl/session.c +++ b/lib/webfuse/adapter/impl/session.c @@ -6,8 +6,12 @@ #include "webfuse/adapter/impl/jsonrpc/request.h" #include "webfuse/adapter/impl/jsonrpc/response.h" +#include "webfuse/core/container_of.h" +#include "webfuse/core/util.h" + #include #include +#include static bool wf_impl_session_send( json_t * request, @@ -20,7 +24,7 @@ static bool wf_impl_session_send( if (result) { - wf_message_queue_push(&session->queue, message); + wf_slist_append(&session->messages, &message->item); lws_callback_on_writable(session->wsi); result = true; @@ -33,31 +37,59 @@ static bool wf_impl_session_send( return result; } -void wf_impl_session_init( - struct wf_impl_session * session, +struct wf_impl_session * wf_impl_session_create( struct lws * wsi, struct wf_impl_authenticators * authenticators, struct wf_impl_timeout_manager * timeout_manager, - struct wf_impl_jsonrpc_server * server) - { - session->wsi = wsi; - session->is_authenticated = false; - session->authenticators = authenticators; - session->server = server; - wf_impl_jsonrpc_proxy_init(&session->rpc, timeout_manager, &wf_impl_session_send, session); - wf_message_queue_init(&session->queue); - } + struct wf_impl_jsonrpc_server * server, + char const * mount_point) +{ + + 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; +} -void wf_impl_session_cleanup( +static void wf_impl_session_dispose_filesystems( + struct wf_slist * filesystems) +{ + struct wf_slist_item * item = filesystems->first; + while (NULL != item) + { + struct wf_slist_item * next = item->next; + struct wf_impl_filesystem * filesystem = WF_CONTAINER_OF(item, struct wf_impl_filesystem, item); + wf_impl_filesystem_dispose(filesystem); + + item = next; + } +} + +void wf_impl_session_dispose( struct wf_impl_session * session) { + wf_impl_session_dispose_filesystems(&session->filesystems); + wf_impl_jsonrpc_proxy_cleanup(&session->rpc); - wf_message_queue_cleanup(&session->queue); + wf_message_queue_cleanup(&session->messages); session->is_authenticated = false; session->wsi = NULL; session->authenticators = NULL; session->server = NULL; -} + free(session->mount_point); + free(session); +} bool wf_impl_session_authenticate( struct wf_impl_session * session, @@ -68,16 +100,27 @@ bool wf_impl_session_authenticate( return session->is_authenticated; } +bool wf_impl_session_add_filesystem( + struct wf_impl_session * session, + char const * name) +{ + struct wf_impl_filesystem * filesystem = wf_impl_filesystem_create(session, name); + wf_slist_append(&session->filesystems, &filesystem->item); + return (NULL != filesystem); +} + + void wf_impl_session_onwritable( struct wf_impl_session * session) { - if (!wf_message_queue_empty(&session->queue)) - { - struct wf_message * message = wf_message_queue_pop(&session->queue); + if (!wf_slist_empty(&session->messages)) + { + struct wf_slist_item * item = wf_slist_remove_first(&session->messages); + struct wf_message * message = WF_CONTAINER_OF(item, struct wf_message, item); lws_write(session->wsi, (unsigned char*) message->data, message->length, LWS_WRITE_TEXT); wf_message_dispose(message); - if (!wf_message_queue_empty(&session->queue)) + if (!wf_slist_empty(&session->messages)) { lws_callback_on_writable(session->wsi); } @@ -105,4 +148,48 @@ void wf_impl_session_receive( json_decref(message); } -} \ No newline at end of file +} + +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); + } +} diff --git a/lib/webfuse/adapter/impl/session.h b/lib/webfuse/adapter/impl/session.h index 116e196..cb1a19d 100644 --- a/lib/webfuse/adapter/impl/session.h +++ b/lib/webfuse/adapter/impl/session.h @@ -12,6 +12,8 @@ using std::size_t; #include "webfuse/core/message_queue.h" #include "webfuse/adapter/impl/jsonrpc/proxy.h" #include "webfuse/adapter/impl/jsonrpc/server.h" +#include "webfuse/adapter/impl/filesystem.h" +#include "webfuse/core/slist.h" #ifdef __cplusplus extern "C" @@ -26,25 +28,35 @@ struct wf_impl_timeout_manager; struct wf_impl_session { + struct wf_slist_item item; + char * mount_point; struct lws * wsi; bool is_authenticated; - struct wf_message_queue queue; + struct wf_slist messages; struct wf_impl_authenticators * authenticators; struct wf_impl_jsonrpc_server * server; struct wf_impl_jsonrpc_proxy rpc; + struct wf_slist filesystems; }; -extern void wf_impl_session_init( - struct wf_impl_session * session, +extern struct wf_impl_session * wf_impl_session_create( struct lws * wsi, - struct wf_impl_authenticators * authenticators, - struct wf_impl_timeout_manager * timeout_manager, - struct wf_impl_jsonrpc_server * server); + struct wf_impl_authenticators * authenticators, + struct wf_impl_timeout_manager * timeout_manager, + struct wf_impl_jsonrpc_server * server, + char const * mount_point); + +extern void wf_impl_session_dispose( + struct wf_impl_session * session); extern bool wf_impl_session_authenticate( struct wf_impl_session * session, struct wf_credentials * creds); +extern bool wf_impl_session_add_filesystem( + struct wf_impl_session * session, + char const * name); + extern void wf_impl_session_receive( struct wf_impl_session * session, char const * data, @@ -53,8 +65,14 @@ extern void wf_impl_session_receive( extern void wf_impl_session_onwritable( struct wf_impl_session * session); -extern void wf_impl_session_cleanup( - struct wf_impl_session * session); +extern bool wf_impl_session_contains_wsi( + struct wf_impl_session * session, + struct lws * wsi); + +extern void wf_impl_session_process_filesystem_request( + struct wf_impl_session * session, + struct lws * wsi); + #ifdef __cplusplus } diff --git a/lib/webfuse/adapter/impl/session_manager.c b/lib/webfuse/adapter/impl/session_manager.c index be87c2a..e93b6c7 100644 --- a/lib/webfuse/adapter/impl/session_manager.c +++ b/lib/webfuse/adapter/impl/session_manager.c @@ -1,17 +1,26 @@ #include "webfuse/adapter/impl/session_manager.h" #include "webfuse/core/util.h" +#include "webfuse/core/container_of.h" #include void wf_impl_session_manager_init( struct wf_impl_session_manager * manager) { - wf_impl_session_init(&manager->session, NULL, NULL, NULL, NULL); + wf_slist_init(&manager->sessions); } void wf_impl_session_manager_cleanup( struct wf_impl_session_manager * manager) { - wf_impl_session_cleanup(&manager->session); + struct wf_slist_item * item = manager->sessions.first; + while (NULL != item) + { + struct wf_slist_item * next = item->next; + struct wf_impl_session * session = WF_CONTAINER_OF(item, struct wf_impl_session, item); + wf_impl_session_dispose(session); + + item = next; + } } struct wf_impl_session * wf_impl_session_manager_add( @@ -19,13 +28,14 @@ struct wf_impl_session * wf_impl_session_manager_add( struct lws * wsi, struct wf_impl_authenticators * authenticators, struct wf_impl_timeout_manager * timeout_manager, - struct wf_impl_jsonrpc_server * server) + struct wf_impl_jsonrpc_server * server, + char const * mount_point) { - struct wf_impl_session * session = NULL; - if (NULL == manager->session.wsi) + struct wf_impl_session * session = wf_impl_session_create( + wsi, authenticators, timeout_manager, server, mount_point); + if (NULL != session) { - session = &manager->session; - wf_impl_session_init(&manager->session, wsi, authenticators, timeout_manager, server); + wf_slist_append(&manager->sessions, &session->item); } return session; @@ -36,37 +46,42 @@ struct wf_impl_session * wf_impl_session_manager_get( struct lws * wsi) { struct wf_impl_session * session = NULL; - if (wsi == manager->session.wsi) - { - session = &manager->session; - } - return session; -} + struct wf_slist_item * item = manager->sessions.first; + while (NULL != item) + { + struct wf_slist_item * next = item->next; + struct wf_impl_session * current = WF_CONTAINER_OF(item, struct wf_impl_session, item); -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 + if (wf_impl_session_contains_wsi(current, wsi)) { + session = current; + break; + } - struct wf_impl_session * session = NULL; - if (NULL != manager->session.wsi) - { - session = &manager->session; + item = next; } return session; } - void wf_impl_session_manager_remove( struct wf_impl_session_manager * manager, struct lws * wsi) { - if (wsi == manager->session.wsi) + struct wf_slist_item * item = manager->sessions.first; + struct wf_slist_item * prev = NULL; + while (NULL != item) { - wf_impl_session_cleanup(&manager->session); - manager->session.wsi = NULL; + struct wf_slist_item * next = item->next; + struct wf_impl_session * session = WF_CONTAINER_OF(item, struct wf_impl_session, item); + if (wsi == session->wsi) + { + wf_slist_remove_after(&manager->sessions, prev); + wf_impl_session_dispose(session); + break; + } + + prev = item; + item = next; } } diff --git a/lib/webfuse/adapter/impl/session_manager.h b/lib/webfuse/adapter/impl/session_manager.h index 6b7a26b..bfe4aa3 100644 --- a/lib/webfuse/adapter/impl/session_manager.h +++ b/lib/webfuse/adapter/impl/session_manager.h @@ -7,6 +7,7 @@ #include "webfuse/adapter/impl/session.h" #include "webfuse/adapter/impl/fuse_wrapper.h" +#include "webfuse/core/slist.h" #ifdef __cplusplus extern "C" @@ -19,7 +20,7 @@ struct wf_impl_jsonrpc_server; struct wf_impl_session_manager { - struct wf_impl_session session; + struct wf_slist sessions; }; extern void wf_impl_session_manager_init( @@ -33,16 +34,13 @@ extern struct wf_impl_session * wf_impl_session_manager_add( struct lws * wsi, struct wf_impl_authenticators * authenticators, struct wf_impl_timeout_manager * timeout_manager, - struct wf_impl_jsonrpc_server * server); + struct wf_impl_jsonrpc_server * server, + char const * mount_point); extern struct wf_impl_session * wf_impl_session_manager_get( struct wf_impl_session_manager * manager, struct lws * wsi); -extern struct wf_impl_session * wf_impl_session_manager_get_by_inode( - struct wf_impl_session_manager * session_manger, - fuse_ino_t inode); - extern void wf_impl_session_manager_remove( struct wf_impl_session_manager * manager, struct lws * wsi); diff --git a/lib/webfuse/core/container_of.h b/lib/webfuse/core/container_of.h new file mode 100644 index 0000000..fa51a26 --- /dev/null +++ b/lib/webfuse/core/container_of.h @@ -0,0 +1,13 @@ +#ifndef WF_CONTAINER_OF_H +#define WF_CONTAINER_OF_H + +#ifndef __cplusplus +#include +#else +#include +#endif + +#define WF_CONTAINER_OF(pointer, type, member) \ + (type *) (((char *) pointer) - offsetof(type, member)) + +#endif diff --git a/lib/webfuse/core/message.c b/lib/webfuse/core/message.c index bc19248..e94d1c6 100644 --- a/lib/webfuse/core/message.c +++ b/lib/webfuse/core/message.c @@ -16,7 +16,6 @@ extern struct wf_message * wf_message_create(json_t const * value) { message->data = &data[sizeof(struct wf_message) + LWS_PRE]; message->length = length; - message->next = NULL; json_dumpb(value, message->data, length, JSON_COMPACT); } diff --git a/lib/webfuse/core/message.h b/lib/webfuse/core/message.h index ea73e5f..7bd346b 100644 --- a/lib/webfuse/core/message.h +++ b/lib/webfuse/core/message.h @@ -9,10 +9,11 @@ using std::size_t; #endif #include +#include "webfuse/core/slist.h" struct wf_message { - struct wf_message * next; + struct wf_slist_item item; char * data; size_t length; }; @@ -22,7 +23,8 @@ extern "C" { #endif -extern struct wf_message * wf_message_create(json_t const * value); +extern struct wf_message * wf_message_create( + json_t const * value); extern void wf_message_dispose( struct wf_message * message); diff --git a/lib/webfuse/core/message_queue.c b/lib/webfuse/core/message_queue.c index b601215..3853f44 100644 --- a/lib/webfuse/core/message_queue.c +++ b/lib/webfuse/core/message_queue.c @@ -1,63 +1,17 @@ #include "webfuse/core/message_queue.h" #include "webfuse/core/message.h" - -void wf_message_queue_init( - struct wf_message_queue * queue) -{ - queue->first = NULL; - queue->last = NULL; - -} +#include "webfuse/core/container_of.h" void wf_message_queue_cleanup( - struct wf_message_queue * queue) + struct wf_slist * queue) { - struct wf_message * message = queue->first; - while (NULL != message) + struct wf_slist_item * item = queue->first; + while (NULL != item) { - struct wf_message * next = message->next; + struct wf_slist_item * next = item->next; + struct wf_message * message = WF_CONTAINER_OF(item, struct wf_message, item); wf_message_dispose(message); - message = next; + item = next; } - wf_message_queue_init(queue); -} - -bool wf_message_queue_empty( - struct wf_message_queue * queue) -{ - return (NULL == queue->first); -} - -void wf_message_queue_push( - struct wf_message_queue * queue, - struct wf_message * message) -{ - message->next = NULL; - - if (NULL != queue->last) - { - queue->last->next = message; - queue->last = message; - } - else - { - queue->first = message; - queue->last = message; - } -} - -struct wf_message * wf_message_queue_pop( - struct wf_message_queue * queue) -{ - struct wf_message * const result = queue->first; - if (NULL != result) - { - queue->first = queue->first->next; - if (NULL == queue->first) - { - queue->last = NULL; - } - } - - return result; + wf_slist_init(queue); } diff --git a/lib/webfuse/core/message_queue.h b/lib/webfuse/core/message_queue.h index 48fdfad..9d549d1 100644 --- a/lib/webfuse/core/message_queue.h +++ b/lib/webfuse/core/message_queue.h @@ -1,39 +1,16 @@ #ifndef WF_MESSAGE_QUEUE_H #define WF_MESSAGE_QUEUE_H -#ifndef __cplusplus -#include -#endif - -struct wf_message_queue; -struct wf_message; - -struct wf_message_queue -{ - struct wf_message * first; - struct wf_message * last; -}; - #ifdef __cplusplus extern "C" { #endif -extern void wf_message_queue_init( - struct wf_message_queue * queue); +struct wf_slist; extern void wf_message_queue_cleanup( - struct wf_message_queue * queue); - -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); + struct wf_slist * queue); -extern struct wf_message * wf_message_queue_pop( - struct wf_message_queue * queue); #ifdef __cplusplus } diff --git a/lib/webfuse/core/slist.c b/lib/webfuse/core/slist.c new file mode 100644 index 0000000..fdc1724 --- /dev/null +++ b/lib/webfuse/core/slist.c @@ -0,0 +1,77 @@ +#include "webfuse/core/slist.h" +#include + +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; +} diff --git a/lib/webfuse/core/slist.h b/lib/webfuse/core/slist.h new file mode 100644 index 0000000..5073ff3 --- /dev/null +++ b/lib/webfuse/core/slist.h @@ -0,0 +1,45 @@ +#ifndef WF_SLIST_H +#define WF_SLIST_H + +#ifndef __cplusplus +#include +#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 diff --git a/lib/webfuse/core/string.c b/lib/webfuse/core/string.c new file mode 100644 index 0000000..b5ead74 --- /dev/null +++ b/lib/webfuse/core/string.c @@ -0,0 +1,35 @@ +#include "webfuse/core/string.h" + +#include +#include + +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; +} diff --git a/lib/webfuse/core/string.h b/lib/webfuse/core/string.h new file mode 100644 index 0000000..00a686b --- /dev/null +++ b/lib/webfuse/core/string.h @@ -0,0 +1,21 @@ +#ifndef WF_CORE_STRING_H +#define WF_CORE_STRING_H + +#ifndef __cplusplus +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern char * wf_create_string(char const * format, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/provider/impl/client_protocol.c b/lib/webfuse/provider/impl/client_protocol.c index f6c34be..a5dd360 100644 --- a/lib/webfuse/provider/impl/client_protocol.c +++ b/lib/webfuse/provider/impl/client_protocol.c @@ -10,6 +10,8 @@ #include "webfuse/provider/impl/provider.h" #include "webfuse/core/util.h" #include "webfuse/core/message.h" +#include "webfuse/core/message_queue.h" +#include "webfuse/core/container_of.h" static void wfp_impl_client_protocol_respond( json_t * response, @@ -20,7 +22,7 @@ static void wfp_impl_client_protocol_respond( struct wf_message * message = wf_message_create(response); if (NULL != message) { - wf_message_queue_push(&protocol->queue, message); + wf_slist_append(&protocol->messages, &message->item); lws_callback_on_writable(protocol->wsi); } } @@ -45,6 +47,26 @@ static void wfp_impl_client_protocol_process_request( } } +static void wfp_impl_client_protocol_add_filesystem( + struct wfp_client_protocol * protocol) +{ + json_t * params = json_array(); + json_array_append_new(params, json_string("cprovider")); + + json_t * request = json_object(); + json_object_set_new(request, "method", json_string("add_filesystem")); + json_object_set_new(request, "params", params); + json_object_set_new(request, "id", json_integer(42)); + + struct wf_message * message = wf_message_create(request); + if (NULL != message) + { + wf_slist_append(&protocol->messages, &message->item); + lws_callback_on_writable(protocol->wsi); + } + + json_decref(request); +} static int wfp_impl_client_protocol_callback( struct lws * wsi, @@ -61,6 +83,7 @@ static int wfp_impl_client_protocol_callback( switch (reason) { case LWS_CALLBACK_CLIENT_ESTABLISHED: + wfp_impl_client_protocol_add_filesystem(protocol); protocol->provider.connected(protocol->user_data); break; case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: @@ -75,13 +98,14 @@ static int wfp_impl_client_protocol_callback( case LWS_CALLBACK_SERVER_WRITEABLE: // fall-through case LWS_CALLBACK_CLIENT_WRITEABLE: - if ((wsi == protocol->wsi) && (!wf_message_queue_empty(&protocol->queue))) + if ((wsi == protocol->wsi) && (!wf_slist_empty(&protocol->messages))) { - struct wf_message * message = wf_message_queue_pop(&protocol->queue); + struct wf_slist_item * item = wf_slist_remove_first(&protocol->messages); + struct wf_message * message = WF_CONTAINER_OF(item, struct wf_message, item); lws_write(wsi, (unsigned char*) message->data, message->length, LWS_WRITE_TEXT); wf_message_dispose(message); - if (!wf_message_queue_empty(&protocol->queue)) + if (!wf_slist_empty(&protocol->messages)) { lws_callback_on_writable(wsi); @@ -102,7 +126,7 @@ void wfp_impl_client_protocol_init( struct wfp_provider const * provider, void * user_data) { - wf_message_queue_init(&protocol->queue); + wf_slist_init(&protocol->messages); protocol->wsi = NULL; @@ -116,7 +140,7 @@ void wfp_impl_client_protocol_init( void wfp_impl_client_protocol_cleanup( struct wfp_client_protocol * protocol) { - wf_message_queue_cleanup(&protocol->queue); + wf_message_queue_cleanup(&protocol->messages); } struct wfp_client_protocol * wfp_impl_client_protocol_create( diff --git a/lib/webfuse/provider/impl/client_protocol.h b/lib/webfuse/provider/impl/client_protocol.h index c4181fe..54929c3 100644 --- a/lib/webfuse/provider/impl/client_protocol.h +++ b/lib/webfuse/provider/impl/client_protocol.h @@ -4,7 +4,7 @@ #include "webfuse/provider/impl/provider.h" #include "webfuse/provider/impl/request.h" -#include "webfuse/core/message_queue.h" +#include "webfuse/core/slist.h" #ifdef __cplusplus extern "C" @@ -20,7 +20,7 @@ struct wfp_client_protocol struct wfp_provider provider; void * user_data; struct lws * wsi; - struct wf_message_queue queue; + struct wf_slist messages; }; extern void wfp_impl_client_protocol_init( diff --git a/lib/webfuse/provider/impl/operation/close.c b/lib/webfuse/provider/impl/operation/close.c index 2dfaf9f..3346fbc 100644 --- a/lib/webfuse/provider/impl/operation/close.c +++ b/lib/webfuse/provider/impl/operation/close.c @@ -8,11 +8,11 @@ void wfp_impl_close( int WF_UNUSED_PARAM(id)) { size_t const param_count = json_array_size(params); - if (3 == param_count) + if (4 == param_count) { - json_t * inode_holder = json_array_get(params, 0); - json_t * handle_holder = json_array_get(params, 1); - json_t * flags_holder = json_array_get(params, 2); + json_t * inode_holder = json_array_get(params, 1); + json_t * handle_holder = json_array_get(params, 2); + json_t * flags_holder = json_array_get(params, 3); if (json_is_integer(inode_holder) && json_is_integer(handle_holder) && diff --git a/lib/webfuse/provider/impl/operation/getattr.c b/lib/webfuse/provider/impl/operation/getattr.c index f83c851..467e99b 100644 --- a/lib/webfuse/provider/impl/operation/getattr.c +++ b/lib/webfuse/provider/impl/operation/getattr.c @@ -13,9 +13,9 @@ void wfp_impl_getattr( int id) { size_t const count = json_array_size(params); - if (1 == count) + if (2 == count) { - json_t * inode_holder = json_array_get(params, 0); + json_t * inode_holder = json_array_get(params, 1); if (json_is_integer(inode_holder)) { diff --git a/lib/webfuse/provider/impl/operation/lookup.c b/lib/webfuse/provider/impl/operation/lookup.c index 17dc2c1..d2e89aa 100644 --- a/lib/webfuse/provider/impl/operation/lookup.c +++ b/lib/webfuse/provider/impl/operation/lookup.c @@ -12,10 +12,10 @@ void wfp_impl_lookup( int id) { size_t const count = json_array_size(params); - if (2 == count) + if (3 == count) { - json_t * inode_holder = json_array_get(params, 0); - json_t * name_holder = json_array_get(params, 1); + json_t * inode_holder = json_array_get(params, 1); + json_t * name_holder = json_array_get(params, 2); if (json_is_integer(inode_holder) && json_is_string(name_holder)) diff --git a/lib/webfuse/provider/impl/operation/open.c b/lib/webfuse/provider/impl/operation/open.c index 6beb974..25c8dff 100644 --- a/lib/webfuse/provider/impl/operation/open.c +++ b/lib/webfuse/provider/impl/operation/open.c @@ -9,10 +9,10 @@ void wfp_impl_open( int id) { size_t const count = json_array_size(params); - if (2 == count) + if (3 == count) { - json_t * inode_holder = json_array_get(params, 0); - json_t * flags_holder = json_array_get(params, 1); + json_t * inode_holder = json_array_get(params, 1); + json_t * flags_holder = json_array_get(params, 2); if (json_is_integer(inode_holder) && json_is_integer(flags_holder)) diff --git a/lib/webfuse/provider/impl/operation/read.c b/lib/webfuse/provider/impl/operation/read.c index 8ea3801..19a9926 100644 --- a/lib/webfuse/provider/impl/operation/read.c +++ b/lib/webfuse/provider/impl/operation/read.c @@ -13,12 +13,12 @@ void wfp_impl_read( int id) { size_t const count = json_array_size(params); - if (4 == count) + if (5 == count) { - json_t * inode_holder = json_array_get(params, 0); - json_t * handle_holder = json_array_get(params, 1); - json_t * offset_holder = json_array_get(params, 2); - json_t * length_holder = json_array_get(params, 3); + json_t * inode_holder = json_array_get(params, 1); + json_t * handle_holder = json_array_get(params, 2); + json_t * offset_holder = json_array_get(params, 3); + json_t * length_holder = json_array_get(params, 4); if (json_is_integer(inode_holder) && json_is_integer(handle_holder) && diff --git a/lib/webfuse/provider/impl/operation/readdir.c b/lib/webfuse/provider/impl/operation/readdir.c index 96d0362..4fc3db7 100644 --- a/lib/webfuse/provider/impl/operation/readdir.c +++ b/lib/webfuse/provider/impl/operation/readdir.c @@ -10,9 +10,9 @@ void wfp_impl_readdir( int id) { size_t const count = json_array_size(params); - if (1 == count) + if (2 == count) { - json_t * inode_holder = json_array_get(params, 0); + json_t * inode_holder = json_array_get(params, 1); if ((NULL != inode_holder) && (json_is_integer(inode_holder))) { diff --git a/test/test_container_of.cc b/test/test_container_of.cc new file mode 100644 index 0000000..91a96ef --- /dev/null +++ b/test/test_container_of.cc @@ -0,0 +1,29 @@ +#include +#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)); +} \ No newline at end of file diff --git a/test/test_slist.cc b/test/test_slist.cc new file mode 100644 index 0000000..c6995fa --- /dev/null +++ b/test/test_slist.cc @@ -0,0 +1,35 @@ +#include +#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); +} diff --git a/test/test_string.cc b/test/test_string.cc new file mode 100644 index 0000000..17e501a --- /dev/null +++ b/test/test_string.cc @@ -0,0 +1,18 @@ +#include +#include + +#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); +}