diff --git a/CMakeLists.txt b/CMakeLists.txt index 08cc332..082d3b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,7 @@ add_library(webfuse-adapter-static STATIC lib/webfuse/adapter/impl/authenticator.c lib/webfuse/adapter/impl/authenticators.c lib/webfuse/adapter/impl/credentials.c + lib/webfuse/adapter/impl/operations.c lib/webfuse/adapter/impl/time/timepoint.c lib/webfuse/adapter/impl/time/timer.c lib/webfuse/adapter/impl/time/timeout_manager.c @@ -83,6 +84,7 @@ add_library(webfuse-adapter-static STATIC lib/webfuse/adapter/impl/operation/open.c lib/webfuse/adapter/impl/operation/close.c lib/webfuse/adapter/impl/operation/read.c + lib/webfuse/adapter/impl/jsonrpc/proxy.c lib/webfuse/adapter/impl/jsonrpc/server.c lib/webfuse/adapter/impl/jsonrpc/method.c lib/webfuse/adapter/impl/jsonrpc/request.c @@ -185,6 +187,8 @@ install(FILES "${PROJECT_BINARY_DIR}/libwebfuse-provider.pc" DESTINATION lib${LI # examples +pkg_check_modules(OPENSSL REQUIRED openssl) + if(NOT WITHOUT_EXAMPLE) # libuserdb @@ -211,9 +215,9 @@ add_executable(webfused example/daemon/main.c ) -target_link_libraries(webfused PUBLIC webfuse-adapter ${EXTRA_LIBS}) +target_link_libraries(webfused PUBLIC webfuse-adapter userdb ${OPENSSL_LIBRARIES} ${EXTRA_LIBS}) target_include_directories(webfused PUBLIC ${EXTRA_INCLUDE_DIRS}) -target_compile_options(webfused PUBLIC ${EXTRA_CFLAGS}) +target_compile_options(webfused PUBLIC ${C_WARNINGS} ${OPENSSL_CFLAGS_OTHER} ${EXTRA_CFLAGS}) # provider @@ -229,9 +233,6 @@ target_compile_options(webfuse-provider-app PUBLIC ${EXTRA_CFLAGS}) # webfuse-passwd -pkg_check_modules(OPENSSL REQUIRED openssl) - - add_executable(webfuse-passwd example/passwd/main.c ) diff --git a/README.md b/README.md index b5e8dde..a9e0ca7 100644 --- a/README.md +++ b/README.md @@ -31,36 +31,9 @@ To avoid Steps 1 and 2, it would be great to keep the update file entirely in we webfuse solves this problem by using the [WebSocket](https://en.wikipedia.org/wiki/WebSocket) protocol. The emdedded device runs a service, known as webfuse adapter, awaiting incoming connections, e.g. from a web browser. The browser acts as a file system provider, providing the update file to the device. -## Concecpt - - +---------------------+ +-------------+ +------+ - | Filesystem Provider | | webfuse | | user | - | (e.g. Webbrowser) | | daemon | | | - +----------+----------+ +------+------+ +---+--+ - | | | - | +-+-+ | - | | +--+ | - | | | | fuse_mount | - | | +<-+ | - | | | | - | | +--+ | - | | | | start ws | - | | +<-+ | - | +-+-+ | - | | | - +-+-+ connect +-+-+ | - | |--------------->| | | - +-+-+ +-+-+ | - | | | - | +-+-+ ls +-+-+ - +-+-+ readdir | |<------------+ | - | |<---------------+ | | | - | | | | | | - | | readdir_resp | | | | - | +--------------->| | [., ..] | | - +-+-+ | +------------>| | - | +-+-+ +-+-+ - | | | +## Concept + +![concept](doc/concept.png) 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: @@ -146,7 +119,7 @@ A response is used to answer a prior request. There are two kinds of responses: | BAD_BUSY | 4 | resource busy | | BAD_FORMAT | 5 | invalid formt | | BAD_NOENTRY | 101 | invalid entry | -| BAD_NOACCESS | 102 | access not allowed | +| BAD_ACCESS_DENIED | 102 | access not allowed | #### Notification @@ -162,7 +135,7 @@ Notfications are used to inform a receiver about something. Unlike requests, not | method_name | string | name of the method to invoke | | params | array | method specific parameters | -### Requests +### Requests (Adapter -> Provider) #### lookup @@ -300,6 +273,73 @@ Read from an open file. | "identiy" | Use data as is; note that JSON strings are UTF-8 encoded | | "base64" | data is base64 encoded | +### Requests (Provider -> Adapter) + +#### authtenticate + +Authenticate the provider. +If authentication is enabled, a provider must be authenticated by the adapter before the adapter will send any messages. + + fs provider: {"method": "authenticate", "params": [, ], "id": } + webfuse daemon: {"result": {}, "id": } + +| Item | Data type | Description | +| ----------- | ----------| ------------------------------- | +| type | string | authentication type (see below) | +| credentials | object | credentials to authenticate | + +##### authentication types + +- **username**: authenticate via username and password + `{"username": , "password": }` + +## Authentication + +By default, webfuse daemon will redirect each filesystem call to the first connected provider without any authentication. +This might be good for testing purposes or when an external authentication mechanism is used. In some use cases, explicit authentication is needed. Therefore, authentication can be enabled within webfuse daemon. + +When authentication is enabled, filesystem calls are only redirected to a connected provider, after `authenticate` +has succeeded. + +![authenticate](doc/authenticate.png) + +### Enable authentication + +Authentication is enabled, if one or more authenticators are registered via `wf_server_config`. + + static bool authenticate(struct wf_credentials * creds, void * user_data) + { + char const * username = wf_credentials_get(creds, "username"); + char const * password = wf_credentials_get(creds, "password"); + + return ((NULL != username) && (0 == strcmp(username, "bob")) && + (NULL != password) && (0 == strcmp(password, "???"))); + } + + wf_server_config * config = wf_server_config_create(); + wf_server_config_add_authenticator(config, "username", &authenticate, NULL); + + wf_server * server = wf_server_create(config); + //... + +### Authenticator types and credentidals + +Each authenticator is identified by a user defined string, called `type`. The type is provided by the `authenticate` request, so you can define different authenticators for different authentication types, e.g. username, certificate, token. + +Actually, only one type is used: **username** +**It is strongly recommended to prefix custom authenticator types with an underscore (`_`) to avoid name clashes.** + +The `wf_credentials`struct represents a map to access credentials as key-value pairs, where both, key and value, are of type string. + +#### username + +The authenticator type **username** is used to authenticate via username and password. Valid credentials should contain two keys. + +- **username** refers to the name of the user +- **password** refers to the password of the user + +**Note** that no further encryption is done, so this authenticator type should not be used over unencrypted websocket connections. + ## Build and run To install dependencies, see below. @@ -318,7 +358,7 @@ By default, unit tests and example application are enabled. You can disable them - **WITHOUT_TESTS**: disable tests `cmake -DWITHOUT_TESTS=ON ..` -- **WITHOUT_EXAMPLE**: disable example +- **WITHOUT_EXAMPLE**: disable example `cmake -DWITHOUD_EXAMPLE=ON ..` ## Dependencies diff --git a/doc/authenticate.png b/doc/authenticate.png new file mode 100644 index 0000000..b0df17d Binary files /dev/null and b/doc/authenticate.png differ diff --git a/doc/authenticate.uml b/doc/authenticate.uml new file mode 100644 index 0000000..00a5f52 --- /dev/null +++ b/doc/authenticate.uml @@ -0,0 +1,28 @@ +@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 +end + + +group authenticate +provider -> daemon: authenticate(type, credentials) +daemon -> daemon: get_authenticator(type) +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 : [., ..] +end + +@enduml \ No newline at end of file diff --git a/doc/concept.png b/doc/concept.png new file mode 100644 index 0000000..9ab4e90 Binary files /dev/null and b/doc/concept.png differ diff --git a/doc/concept.uml b/doc/concept.uml new file mode 100644 index 0000000..a7ec266 --- /dev/null +++ b/doc/concept.uml @@ -0,0 +1,30 @@ +@startuml +participant "Filesystem Provider\n(e.g. Webbrowser)" as provider +participant "webfuse\ndaemon" as daemon +actor "user" as user + +group startup +daemon -> daemon : fuse_mount +daemon -> daemon : start ws +end +... + +group connect +provider -> daemon : connect + opt authentication + provider -> daemon: authenticate + daemon -> daemon: check credentials + daemon --> provider: okay + end +end +... + +group directory listing +user -> daemon : ls +daemon -> provider : readdir +provider --> daemon : readdir_resp +daemon --> user : [., ..] +end +... + +@enduml \ No newline at end of file diff --git a/example/daemon/main.c b/example/daemon/main.c index f9b4798..1bafb8c 100644 --- a/example/daemon/main.c +++ b/example/daemon/main.c @@ -7,9 +7,9 @@ #include #include -#include #include +#include struct args @@ -50,24 +50,16 @@ static bool authenticate(struct wf_credentials * creds, void * user_data) char const * password = wf_credentials_get(creds, "password"); if ((NULL != username) && (NULL != password)) { - json_t * passwd = json_load_file(args->passwd_path, 0, NULL); - if (NULL != passwd) + struct userdb * db = userdb_create(""); + result = userdb_load(db, args->passwd_path); + if (result) { - json_t * user = json_object_get(passwd, username); - if (json_is_object(user)) - { - json_t * password_holder = json_object_get(user, "password"); - if (json_is_string(password_holder)) - { - result = (0 == strcmp(password, json_string_value(password_holder))); - } - } - - json_decref(passwd); + result = userdb_check(db, username, password); + userdb_dispose(db); } } - return result; + return result; } static int parse_arguments(int argc, char * argv[], struct args * args) diff --git a/example/daemon/www/js/connection_view.js b/example/daemon/www/js/connection_view.js index 37c0247..c46b584 100644 --- a/example/daemon/www/js/connection_view.js +++ b/example/daemon/www/js/connection_view.js @@ -6,20 +6,51 @@ export class ConnectionView { this.element = document.createElement("div"); + const connectBox = document.createElement("div"); + this.element.appendChild(connectBox); + const urlLabel = document.createElement("span"); urlLabel.textContent = "URL:"; - this.element.appendChild(urlLabel); + connectBox.appendChild(urlLabel); this.urlTextbox = document.createElement("input"); this.urlTextbox.type = "text"; this.urlTextbox.value = window.location.href.replace(/^http/, "ws"); - this.element.appendChild(this.urlTextbox); + connectBox.appendChild(this.urlTextbox); this.connectButton = document.createElement("input"); this.connectButton.type = "button"; this.connectButton.value = "connect"; this.connectButton.addEventListener("click", () => { this._onConnectButtonClicked(); }); - this.element.appendChild(this.connectButton); + connectBox.appendChild(this.connectButton); + + + const authenticateBox = document.createElement("div"); + this.element.appendChild(authenticateBox); + + const usernameLabel = document.createElement("span"); + usernameLabel.textContent = "user:"; + authenticateBox.appendChild(usernameLabel); + + this.usernameTextbox = document.createElement("input"); + this.usernameTextbox.type = "text"; + this.usernameTextbox.value = "bob"; + authenticateBox.appendChild(this.usernameTextbox); + + const passwordLabel = document.createElement("span"); + passwordLabel.textContent = "user:"; + authenticateBox.appendChild(passwordLabel); + + this.passwordTextbox = document.createElement("input"); + 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() { @@ -32,6 +63,15 @@ 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() { this.connectButton.value = "disconnect"; } diff --git a/example/daemon/www/js/webfuse/client.js b/example/daemon/www/js/webfuse/client.js index 7c1b2bb..2838732 100644 --- a/example/daemon/www/js/webfuse/client.js +++ b/example/daemon/www/js/webfuse/client.js @@ -24,6 +24,16 @@ export class Client { }; } + authenticate(type, credentials) { + const request = { + "method": "authenticate", + "params": [type, credentials], + "id": 42 + }; + + this._ws.send(JSON.stringify(request)); + } + disconnect() { if (this._ws) { this._ws.close(); diff --git a/example/lib/userdb/src/userdb.c b/example/lib/userdb/src/userdb.c index 172742a..8e5877a 100644 --- a/example/lib/userdb/src/userdb.c +++ b/example/lib/userdb/src/userdb.c @@ -127,9 +127,9 @@ static char * compute_hash( { EVP_MD_CTX * context = EVP_MD_CTX_new(); EVP_DigestInit_ex(context, digest, NULL); - EVP_DigestUpdate(context, password, strlen(password)); - EVP_DigestUpdate(context, salt, strlen(salt)); - EVP_DigestUpdate(context, db->pepper, strlen(db->pepper)); + EVP_DigestUpdate(context, password, strlen(password)); /* Flawfinder: ignore */ + EVP_DigestUpdate(context, salt, strlen(salt)); /* Flawfinder: ignore */ + EVP_DigestUpdate(context, db->pepper, strlen(db->pepper)); /* Flawfinder: ignore */ EVP_DigestFinal_ex(context, hash, &hash_size); EVP_MD_CTX_free(context); diff --git a/example/provider/main.c b/example/provider/main.c index a3d6adb..33290cd 100644 --- a/example/provider/main.c +++ b/example/provider/main.c @@ -268,7 +268,7 @@ static void fs_open( } else { - wfp_respond_error(request, WF_BAD_NOACCESS); + wfp_respond_error(request, WF_BAD_ACCESS_DENIED); } } else diff --git a/include/webfuse/core/status.h b/include/webfuse/core/status.h index 9ce425d..7b3c95f 100644 --- a/include/webfuse/core/status.h +++ b/include/webfuse/core/status.h @@ -10,7 +10,7 @@ #define WF_BAD_FORMAT 5 #define WF_BAD_NOENTRY 101 -#define WF_BAD_NOACCESS 102 +#define WF_BAD_ACCESS_DENIED 102 typedef int wf_status; diff --git a/lib/webfuse/adapter/impl/filesystem.c b/lib/webfuse/adapter/impl/filesystem.c index 2e9cce9..0e52b9f 100644 --- a/lib/webfuse/adapter/impl/filesystem.c +++ b/lib/webfuse/adapter/impl/filesystem.c @@ -1,7 +1,7 @@ #include "webfuse/adapter/impl/filesystem.h" #include "webfuse/adapter/impl/operations.h" -#include "webfuse/adapter/impl/jsonrpc/server.h" +#include #include #include #include @@ -19,7 +19,7 @@ static struct fuse_lowlevel_ops const filesystem_operations = bool wf_impl_filesystem_init( struct wf_impl_filesystem * filesystem, - struct wf_impl_jsonrpc_server * rpc, + struct wf_impl_session_manager * session_manager, char * mount_point) { bool result = false; @@ -29,7 +29,7 @@ bool wf_impl_filesystem_init( filesystem->args.argv = argv; filesystem->args.allocated = 0; - filesystem->user_data.rpc = rpc; + filesystem->user_data.session_manager = session_manager; filesystem->user_data.timeout = 1.0; memset(&filesystem->buffer, 0, sizeof(struct fuse_buf)); diff --git a/lib/webfuse/adapter/impl/filesystem.h b/lib/webfuse/adapter/impl/filesystem.h index 0a3adad..af07c32 100644 --- a/lib/webfuse/adapter/impl/filesystem.h +++ b/lib/webfuse/adapter/impl/filesystem.h @@ -13,7 +13,7 @@ extern "C" { #endif -struct wf_impl_jsonrpc_server; +struct wf_impl_session_manager; struct wf_impl_filesystem { @@ -25,7 +25,7 @@ struct wf_impl_filesystem extern bool wf_impl_filesystem_init( struct wf_impl_filesystem * filesystem, - struct wf_impl_jsonrpc_server * rpc, + struct wf_impl_session_manager * session_manager, char * mount_point); extern void wf_impl_filesystem_cleanup( diff --git a/lib/webfuse/adapter/impl/jsonrpc/method.c b/lib/webfuse/adapter/impl/jsonrpc/method.c index ddc7df5..8485471 100644 --- a/lib/webfuse/adapter/impl/jsonrpc/method.c +++ b/lib/webfuse/adapter/impl/jsonrpc/method.c @@ -1,10 +1,9 @@ -#include "webfuse/adapter/impl/jsonrpc/method_intern.h" - +#include "webfuse/adapter/impl/jsonrpc/method.h" #include #include struct wf_impl_jsonrpc_method * wf_impl_jsonrpc_method_create( - char const * name, + char const * method_name, wf_impl_jsonrpc_method_invoke_fn * invoke, void * user_data) { @@ -12,7 +11,7 @@ struct wf_impl_jsonrpc_method * wf_impl_jsonrpc_method_create( if (NULL != method) { method->next = NULL; - method->name = strdup(name); + method->name = strdup(method_name); method->invoke = invoke; method->user_data = user_data; } diff --git a/lib/webfuse/adapter/impl/jsonrpc/method.h b/lib/webfuse/adapter/impl/jsonrpc/method.h index f102f60..739f692 100644 --- a/lib/webfuse/adapter/impl/jsonrpc/method.h +++ b/lib/webfuse/adapter/impl/jsonrpc/method.h @@ -1,31 +1,40 @@ #ifndef WF_ADAPTER_IMPL_JSONRPC_METHOD_H #define WF_ADAPTER_IMPL_JSONRPC_METHOD_H -#ifndef __cplusplus -#include -#endif - #include -#include "webfuse/core/status.h" #ifdef __cplusplus extern "C" { #endif -typedef bool wf_impl_jsonrpc_method_invoke_fn( - void * user_data, - struct json_t const * method_call); +struct wf_impl_jsonrpc_request; -typedef void wf_impl_jsonrpc_method_finished_fn( - void * user_data, - wf_status status, - struct json_t const * result); +typedef void wf_impl_jsonrpc_method_invoke_fn( + struct wf_impl_jsonrpc_request * request, + char const * method_name, + json_t * params, + void * user_data); + +struct wf_impl_jsonrpc_method +{ + struct wf_impl_jsonrpc_method * next; + char * name; + wf_impl_jsonrpc_method_invoke_fn * invoke; + void * user_data; +}; + +extern struct wf_impl_jsonrpc_method * wf_impl_jsonrpc_method_create( + char const * method_name, + wf_impl_jsonrpc_method_invoke_fn * invoke, + void * user_data); + +extern void wf_impl_jsonrpc_method_dispose( + struct wf_impl_jsonrpc_method * method); #ifdef __cplusplus } #endif - #endif diff --git a/lib/webfuse/adapter/impl/jsonrpc/method_intern.h b/lib/webfuse/adapter/impl/jsonrpc/method_intern.h deleted file mode 100644 index 0a1190d..0000000 --- a/lib/webfuse/adapter/impl/jsonrpc/method_intern.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef WF_ADAPTER_IMPL_JSONRPC_METHOD_INTERN_H -#define WF_ADAPTER_IMPL_JSONRPC_METHOD_INTERN_H - -#include "webfuse/adapter/impl/jsonrpc/method.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - -struct wf_impl_jsonrpc_method -{ - struct wf_impl_jsonrpc_method * next; - char * name; - wf_impl_jsonrpc_method_invoke_fn * invoke; - void * user_data; -}; - -extern struct wf_impl_jsonrpc_method * wf_impl_jsonrpc_method_create( - char const * name, - wf_impl_jsonrpc_method_invoke_fn * invoke, - void * user_data); - -extern void wf_impl_jsonrpc_method_dispose( - struct wf_impl_jsonrpc_method * method); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/lib/webfuse/adapter/impl/jsonrpc/proxy.c b/lib/webfuse/adapter/impl/jsonrpc/proxy.c new file mode 100644 index 0000000..5435562 --- /dev/null +++ b/lib/webfuse/adapter/impl/jsonrpc/proxy.c @@ -0,0 +1,181 @@ +#include "webfuse/adapter/impl/jsonrpc/proxy.h" +#include + +#include "webfuse/adapter/impl/jsonrpc/response.h" + +#define WF_DEFAULT_TIMEOUT (10 * 1000) + +static void wf_impl_jsonrpc_proxy_timeout( + struct wf_impl_timer * timer) +{ + struct wf_impl_jsonrpc_proxy * proxy = timer->user_data; + + if (proxy->request.is_pending) + { + wf_impl_jsonrpc_proxy_finished_fn * finished = proxy->request.finished; + void * user_data = proxy->request.user_data; + + proxy->request.is_pending = false; + proxy->request.id = 0; + proxy->request.user_data = NULL; + proxy->request.finished = NULL; + wf_impl_timer_cancel(&proxy->request.timer); + + finished(user_data, WF_BAD_TIMEOUT, NULL); + } +} + +static json_t * wf_impl_jsonrpc_request_create( + char const * method, + int id, + char const * param_info, + va_list args) +{ + json_t * request = json_object(); + json_object_set_new(request, "method", json_string(method)); + json_t * params = json_array(); + + for (char const * param_type = param_info; '\0' != *param_type; param_type++) + { + switch(*param_type) + { + case 's': + { + char const * const value = va_arg(args, char const *); + json_array_append_new(params, json_string(value)); + } + break; + case 'i': + { + int const value = va_arg(args, int); + json_array_append_new(params, json_integer(value)); + } + break; + default: + fprintf(stderr, "fatal: unknown param_type '%c'\n", *param_type); + exit(EXIT_FAILURE); + break; + } + } + + + json_object_set_new(request, "params", params); + if (0 != id) + { + json_object_set_new(request, "id", json_integer(id)); + } + + return request; +} + +void wf_impl_jsonrpc_proxy_init( + struct wf_impl_jsonrpc_proxy * proxy, + struct wf_impl_timeout_manager * timeout_manager, + wf_impl_jsonrpc_send_fn * send, + void * user_data) +{ + proxy->send = send; + proxy->user_data = user_data; + proxy->request.is_pending = false; + + wf_impl_timer_init(&proxy->request.timer, timeout_manager); +} + +void wf_impl_jsonrpc_proxy_cleanup( + struct wf_impl_jsonrpc_proxy * proxy) +{ + wf_impl_timer_cleanup(&proxy->request.timer); + + if (proxy->request.is_pending) + { + proxy->request.finished(proxy->request.user_data, WF_BAD, NULL); + proxy->request.is_pending = false; + } +} + +void wf_impl_jsonrpc_proxy_invoke( + struct wf_impl_jsonrpc_proxy * proxy, + wf_impl_jsonrpc_proxy_finished_fn * finished, + void * user_data, + char const * method_name, + char const * param_info, + ... +) +{ + if (!proxy->request.is_pending) + { + proxy->request.is_pending = true; + proxy->request.finished = finished; + proxy->request.user_data = user_data; + proxy->request.id = 42; + wf_impl_timer_start(&proxy->request.timer, wf_impl_timepoint_in_msec(WF_DEFAULT_TIMEOUT), + &wf_impl_jsonrpc_proxy_timeout, proxy); + + va_list args; + va_start(args, param_info); + json_t * request = wf_impl_jsonrpc_request_create(method_name, proxy->request.id, param_info, args); + va_end(args); + if (NULL != request) + { + if (!proxy->send(request, proxy->user_data)) + { + proxy->request.is_pending = false; + proxy->request.finished = NULL; + proxy->request.user_data = NULL; + proxy->request.id = 0; + wf_impl_timer_cancel(&proxy->request.timer); + + finished(user_data, WF_BAD, NULL); + } + json_decref(request); + } + } + else + { + finished(user_data, WF_BAD_BUSY, NULL); + } +} + +extern void wf_impl_jsonrpc_proxy_notify( + struct wf_impl_jsonrpc_proxy * proxy, + char const * method_name, + char const * param_info, + ... +) +{ + va_list args; + va_start(args, param_info); + json_t * request = wf_impl_jsonrpc_request_create(method_name, 0, param_info, args); + va_end(args); + if (NULL != request) + { + proxy->send(request, proxy->user_data); + json_decref(request); + } +} + + +void wf_impl_jsonrpc_proxy_onresult( + struct wf_impl_jsonrpc_proxy * proxy, + json_t * message) +{ + struct wf_impl_jsonrpc_response response; + wf_impl_jsonrpc_response_init(&response, message); + + if ((proxy->request.is_pending) && (response.id == proxy->request.id)) + { + wf_impl_jsonrpc_proxy_finished_fn * finished = proxy->request.finished; + void * user_data = proxy->request.user_data; + + proxy->request.is_pending = false; + proxy->request.id = 0; + proxy->request.user_data = NULL; + proxy->request.finished = NULL; + wf_impl_timer_cancel(&proxy->request.timer); + + finished(user_data, response.status, response.result); + } + + wf_impl_jsonrpc_response_cleanup(&response); +} + diff --git a/lib/webfuse/adapter/impl/jsonrpc/proxy.h b/lib/webfuse/adapter/impl/jsonrpc/proxy.h new file mode 100644 index 0000000..92b4508 --- /dev/null +++ b/lib/webfuse/adapter/impl/jsonrpc/proxy.h @@ -0,0 +1,80 @@ +#ifndef WF_ADAPTER_IMPL_JSONRPC_PROXY_H +#define WF_ADAPTER_IMPL_JSONRPC_PROXY_H + +#ifndef __cplusplus +#include +#include +#include +#else +#include +#include +using std::size_t; +#endif + +#include +#include "webfuse/adapter/impl/jsonrpc/send_fn.h" +#include "webfuse/adapter/impl/time/timeout_manager.h" +#include "webfuse/adapter/impl/time/timer.h" +#include "webfuse/core/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void wf_impl_jsonrpc_proxy_finished_fn( + void * user_data, + wf_status status, + struct json_t const * result); + + +struct wf_impl_jsonrpc_request +{ + bool is_pending; + wf_impl_jsonrpc_proxy_finished_fn * finished; + void * user_data; + int id; + struct wf_impl_timer timer; +}; + +struct wf_impl_jsonrpc_proxy +{ + struct wf_impl_jsonrpc_request request; + wf_impl_jsonrpc_send_fn * send; + void * user_data; +}; + +extern void wf_impl_jsonrpc_proxy_init( + struct wf_impl_jsonrpc_proxy * proxy, + struct wf_impl_timeout_manager * manager, + wf_impl_jsonrpc_send_fn * send, + void * user_data); + +extern void wf_impl_jsonrpc_proxy_cleanup( + struct wf_impl_jsonrpc_proxy * proxy); + +extern void wf_impl_jsonrpc_proxy_invoke( + struct wf_impl_jsonrpc_proxy * proxy, + wf_impl_jsonrpc_proxy_finished_fn * finished, + void * user_data, + char const * method_name, + char const * param_info, + ... +); + +extern void wf_impl_jsonrpc_proxy_notify( + struct wf_impl_jsonrpc_proxy * proxy, + char const * method_name, + char const * param_info, + ... +); + +extern void wf_impl_jsonrpc_proxy_onresult( + struct wf_impl_jsonrpc_proxy * proxy, + json_t * message); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/lib/webfuse/adapter/impl/jsonrpc/request.c b/lib/webfuse/adapter/impl/jsonrpc/request.c index 710db11..e016e1c 100644 --- a/lib/webfuse/adapter/impl/jsonrpc/request.c +++ b/lib/webfuse/adapter/impl/jsonrpc/request.c @@ -1,44 +1,82 @@ #include "webfuse/adapter/impl/jsonrpc/request.h" +#include "webfuse/core/status_intern.h" +#include -json_t * wf_impl_jsonrpc_request_create( - char const * method, - int id, - char const * param_info, - va_list args) +struct wf_impl_jsonrpc_request { - json_t * request = json_object(); - json_object_set_new(request, "method", json_string(method)); - json_t * params = json_array(); - - for (char const * param_type = param_info; '\0' != *param_type; param_type++) - { - switch(*param_type) - { - case 's': - { - char const * const value = va_arg(args, char const *); - json_array_append_new(params, json_string(value)); - } - break; - case 'i': - { - int const value = va_arg(args, int); - json_array_append_new(params, json_integer(value)); - } - break; - default: - fprintf(stderr, "fatal: unknown param_type '%c'\n", *param_type); - exit(EXIT_FAILURE); - break; - } - } - - - json_object_set_new(request, "params", params); - if (0 != id) - { - json_object_set_new(request, "id", json_integer(id)); - } - - return request; + int id; + wf_impl_jsonrpc_send_fn * send; + void * user_data; +}; + +bool wf_impl_jsonrpc_is_request( + json_t * message) +{ + json_t * id = json_object_get(message, "id"); + json_t * method = json_object_get(message, "method"); + json_t * params = json_object_get(message, "params"); + + return (json_is_integer(id) && json_is_string(method) && + (json_is_array(params) || json_is_object(params))); } + + +struct wf_impl_jsonrpc_request * wf_impl_jsonrpc_request_create( + int id, + wf_impl_jsonrpc_send_fn * send, + void * user_data) +{ + struct wf_impl_jsonrpc_request * request = malloc(sizeof(struct wf_impl_jsonrpc_request)); + if (NULL != request) + { + request->id = id; + request->send = send; + request->user_data = user_data; + } + + return request; +} + +void wf_impl_jsonrpc_request_dispose( + struct wf_impl_jsonrpc_request * request) +{ + free(request); +} + +void * wf_impl_jsonrpc_request_get_userdata( + struct wf_impl_jsonrpc_request * request) +{ + return request->user_data; +} + + +void wf_impl_jsonrpc_respond( + struct wf_impl_jsonrpc_request * request, + json_t * result) +{ + json_t * response = json_object(); + json_object_set_new(response, "result", result); + json_object_set_new(response, "id", json_integer(request->id)); + + request->send(response, request->user_data); + json_decref(response); + wf_impl_jsonrpc_request_dispose(request); +} + +void wf_impl_jsonrpc_respond_error( + struct wf_impl_jsonrpc_request * request, + wf_status status) +{ + json_t * err = json_object(); + json_object_set_new(err, "code", json_integer(status)); + json_object_set_new(err, "message", json_string(wf_status_tostring(status))); + + json_t * response = json_object(); + json_object_set_new(response, "error", err); + json_object_set_new(response, "id", json_integer(request->id)); + + request->send(response, request->user_data); + json_decref(response); + wf_impl_jsonrpc_request_dispose(request); +} + diff --git a/lib/webfuse/adapter/impl/jsonrpc/request.h b/lib/webfuse/adapter/impl/jsonrpc/request.h index 28c9f3d..7d26228 100644 --- a/lib/webfuse/adapter/impl/jsonrpc/request.h +++ b/lib/webfuse/adapter/impl/jsonrpc/request.h @@ -12,17 +12,38 @@ using std::size_t; #endif #include +#include "webfuse/core/status.h" +#include "webfuse/adapter/impl/jsonrpc/send_fn.h" #ifdef __cplusplus extern "C" { #endif -extern json_t * wf_impl_jsonrpc_request_create( - char const * method, - int id, - char const * param_info, - va_list args); +struct wf_impl_jsonrpc_request; + +extern bool wf_impl_jsonrpc_is_request( + json_t * message); + +extern struct wf_impl_jsonrpc_request * wf_impl_jsonrpc_request_create( + int id, + wf_impl_jsonrpc_send_fn * send, + void * user_data); + +extern void wf_impl_jsonrpc_request_dispose( + struct wf_impl_jsonrpc_request * request); + +extern void * wf_impl_jsonrpc_request_get_userdata( + struct wf_impl_jsonrpc_request * request); + +extern void wf_impl_jsonrpc_respond( + struct wf_impl_jsonrpc_request * request, + json_t * result); + +extern void wf_impl_jsonrpc_respond_error( + struct wf_impl_jsonrpc_request * request, + wf_status status); + #ifdef __cplusplus } diff --git a/lib/webfuse/adapter/impl/jsonrpc/response.c b/lib/webfuse/adapter/impl/jsonrpc/response.c index 74f230f..00eb396 100644 --- a/lib/webfuse/adapter/impl/jsonrpc/response.c +++ b/lib/webfuse/adapter/impl/jsonrpc/response.c @@ -1,26 +1,29 @@ #include "webfuse/adapter/impl/jsonrpc/response.h" +extern bool wf_impl_jsonrpc_is_response( + json_t * message) +{ + json_t * id = json_object_get(message, "id"); + json_t * err = json_object_get(message, "error"); + json_t * result = json_object_get(message, "result"); + + return (json_is_integer(id) && + (json_is_object(err) || (NULL != result))); +} + + void wf_impl_jsonrpc_response_init( struct wf_impl_jsonrpc_response * result, - char const * buffer, - size_t length) + json_t * response) { result->status = WF_BAD; result->id = -1; result->result = NULL; - json_t * response = json_loadb(buffer, length, 0, NULL); - if (NULL == response) - { - result->status = WF_BAD_FORMAT; - return; - } - json_t * id_holder = json_object_get(response, "id"); if ((NULL == id_holder) || (!json_is_integer(id_holder))) { result->status = WF_BAD_FORMAT; - json_decref(response); return; } @@ -45,8 +48,6 @@ void wf_impl_jsonrpc_response_init( } } } - - json_decref(response); } void wf_impl_jsonrpc_response_cleanup( diff --git a/lib/webfuse/adapter/impl/jsonrpc/response.h b/lib/webfuse/adapter/impl/jsonrpc/response.h index b537994..d53f667 100644 --- a/lib/webfuse/adapter/impl/jsonrpc/response.h +++ b/lib/webfuse/adapter/impl/jsonrpc/response.h @@ -2,6 +2,7 @@ #define WF_ADAPTER_IMPL_JSONRPC_RESPONSE_H #ifndef __cplusplus +#include #include #else #include @@ -22,10 +23,12 @@ struct wf_impl_jsonrpc_response json_t * result; }; +extern bool wf_impl_jsonrpc_is_response( + json_t * message); + extern void wf_impl_jsonrpc_response_init( struct wf_impl_jsonrpc_response * response, - char const * buffer, - size_t buffer_length); + json_t * message); extern void wf_impl_jsonrpc_response_cleanup( struct wf_impl_jsonrpc_response * response); diff --git a/lib/webfuse/adapter/impl/jsonrpc/send_fn.h b/lib/webfuse/adapter/impl/jsonrpc/send_fn.h new file mode 100644 index 0000000..baeedf7 --- /dev/null +++ b/lib/webfuse/adapter/impl/jsonrpc/send_fn.h @@ -0,0 +1,23 @@ +#ifndef WF_ADAPTER_IMPL_JSONRPC_SEND_FN_H +#define WF_ADAPTER_IMPL_JSONRPC_SEND_FN_H + +#ifndef __cplusplus +#include +#endif + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef bool wf_impl_jsonrpc_send_fn( + json_t * request, + void * user_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/adapter/impl/jsonrpc/server.c b/lib/webfuse/adapter/impl/jsonrpc/server.c index 613e27d..717d71b 100644 --- a/lib/webfuse/adapter/impl/jsonrpc/server.c +++ b/lib/webfuse/adapter/impl/jsonrpc/server.c @@ -1,186 +1,95 @@ #include "webfuse/adapter/impl/jsonrpc/server.h" -#include - -#include "webfuse/adapter/impl/jsonrpc/method_intern.h" +#include "webfuse/adapter/impl/jsonrpc/method.h" #include "webfuse/adapter/impl/jsonrpc/request.h" -#include "webfuse/adapter/impl/jsonrpc/response.h" - -#define WF_DEFAULT_TIMEOUT (10 * 1000) +#include "webfuse/core/util.h" -static struct wf_impl_jsonrpc_method const * wf_impl_jsonrpc_server_getmethod( - struct wf_impl_jsonrpc_server * server, - char const * name) -{ - struct wf_impl_jsonrpc_method * method = server->methods; - while ((NULL != method) && (0 == strcmp(name, method->name))) - { - method = method->next; - } - - return method; -} - -static void wf_impl_jsonrpc_server_timeout( - struct wf_impl_timer * timer) -{ - struct wf_impl_jsonrpc_server * server = timer->user_data; - - if (server->request.is_pending) - { - wf_impl_jsonrpc_method_finished_fn * finished = server->request.finished; - void * user_data = server->request.user_data; - - server->request.is_pending = false; - server->request.id = 0; - server->request.user_data = NULL; - server->request.finished = NULL; - wf_impl_timer_cancel(&server->request.timer); - - finished(user_data, WF_BAD_TIMEOUT, NULL); - } -} +#include void wf_impl_jsonrpc_server_init( - struct wf_impl_jsonrpc_server * server, - struct wf_impl_timeout_manager * timeout_manager) + struct wf_impl_jsonrpc_server * server) { server->methods = NULL; - server->request.is_pending = false; - - wf_impl_timer_init(&server->request.timer, timeout_manager); } void wf_impl_jsonrpc_server_cleanup( struct wf_impl_jsonrpc_server * server) { - wf_impl_timer_cleanup(&server->request.timer); - - if (server->request.is_pending) + struct wf_impl_jsonrpc_method * current = server->methods; + while (NULL != current) { - server->request.finished(server->request.user_data, WF_BAD, NULL); - server->request.is_pending = false; - } - - struct wf_impl_jsonrpc_method * method = server->methods; - while (NULL != method) - { - struct wf_impl_jsonrpc_method * next = method->next; - method->next = NULL; - wf_impl_jsonrpc_method_dispose(method); - method = next; + struct wf_impl_jsonrpc_method * next = current->next; + wf_impl_jsonrpc_method_dispose(current); + current = next; } server->methods = NULL; } void wf_impl_jsonrpc_server_add( struct wf_impl_jsonrpc_server * server, - char const * name, + char const * method_name, wf_impl_jsonrpc_method_invoke_fn * invoke, void * user_data) { - struct wf_impl_jsonrpc_method * method = wf_impl_jsonrpc_method_create(name, invoke, user_data); + struct wf_impl_jsonrpc_method * method = wf_impl_jsonrpc_method_create(method_name, invoke, user_data); method->next = server->methods; server->methods = method; } -void wf_impl_jsonrpc_server_invoke( - struct wf_impl_jsonrpc_server * server, - wf_impl_jsonrpc_method_finished_fn * finished, - void * user_data, - char const * method_name, - char const * param_info, - ... -) +static void wf_impl_jsonrpc_server_invalid_method_invoke( + struct wf_impl_jsonrpc_request * request, + char const * WF_UNUSED_PARAM(method_name), + json_t * WF_UNUSED_PARAM(params), + void * WF_UNUSED_PARAM(user_data)) { - if (!server->request.is_pending) - { - struct wf_impl_jsonrpc_method const * method = wf_impl_jsonrpc_server_getmethod(server, method_name); - if (NULL != method) - { - server->request.is_pending = true; - server->request.finished = finished; - server->request.user_data = user_data; - server->request.id = 42; - wf_impl_timer_start(&server->request.timer, wf_impl_timepoint_in_msec(WF_DEFAULT_TIMEOUT), - &wf_impl_jsonrpc_server_timeout, server); - - va_list args; - va_start(args, param_info); - json_t * request = wf_impl_jsonrpc_request_create(method_name, server->request.id, param_info, args); - va_end(args); - if (NULL != request) - { - if (!method->invoke(method->user_data, request)) - { - server->request.is_pending = false; - server->request.finished = NULL; - server->request.user_data = NULL; - server->request.id = 0; - wf_impl_timer_cancel(&server->request.timer); - - finished(user_data, WF_BAD, NULL); - } - json_decref(request); - } - } - else - { - finished(user_data, WF_BAD_NOTIMPLEMENTED, NULL); - } - } - else - { - finished(user_data, WF_BAD_BUSY, NULL); - } + wf_impl_jsonrpc_respond_error(request, WF_BAD_NOTIMPLEMENTED); } -extern void wf_impl_jsonrpc_server_notify( - struct wf_impl_jsonrpc_server * server, - char const * method_name, - char const * param_info, - ... -) +static struct wf_impl_jsonrpc_method const wf_impl_jsonrpc_server_invalid_method = { - struct wf_impl_jsonrpc_method const * method = wf_impl_jsonrpc_server_getmethod(server, method_name); - if (NULL != method) + .next = NULL, + .name = "", + .invoke = &wf_impl_jsonrpc_server_invalid_method_invoke, + .user_data = NULL +}; + +static struct wf_impl_jsonrpc_method const * wf_impl_jsonrpc_server_get_method( + struct wf_impl_jsonrpc_server * server, + char const * method_name) +{ + struct wf_impl_jsonrpc_method const * current = server->methods; + while (NULL != current) + { + if (0 == strcmp(method_name, current->name)) { - - va_list args; - va_start(args, param_info); - json_t * request = wf_impl_jsonrpc_request_create(method_name, 0, param_info, args); - va_end(args); - if (NULL != request) - { - method->invoke(method->user_data, request); - json_decref(request); - } + return current; } -} + current = current->next; + } + return &wf_impl_jsonrpc_server_invalid_method; +} -void wf_impl_jsonrpc_server_onresult( +void wf_impl_jsonrpc_server_process( struct wf_impl_jsonrpc_server * server, - char const * message, - size_t length) + json_t * request_data, + wf_impl_jsonrpc_send_fn * send, + void * user_data) { - struct wf_impl_jsonrpc_response response; - wf_impl_jsonrpc_response_init(&response, message, length); + json_t * method_holder = json_object_get(request_data, "method"); + json_t * params = json_object_get(request_data, "params"); + json_t * id_holder = json_object_get(request_data, "id"); - if ((server->request.is_pending) && (response.id == server->request.id)) + if (json_is_string(method_holder) && + (json_is_array(params) || (json_is_object(params))) && + json_is_integer(id_holder)) { - wf_impl_jsonrpc_method_finished_fn * finished = server->request.finished; - void * user_data = server->request.user_data; + char const * method_name = json_string_value(method_holder); + int id = json_integer_value(id_holder); + struct wf_impl_jsonrpc_request * request = wf_impl_jsonrpc_request_create(id, send, user_data); + struct wf_impl_jsonrpc_method const * method = wf_impl_jsonrpc_server_get_method(server, method_name); - server->request.is_pending = false; - server->request.id = 0; - server->request.user_data = NULL; - server->request.finished = NULL; - wf_impl_timer_cancel(&server->request.timer); - - finished(user_data, response.status, response.result); + method->invoke(request, method_name, params, method->user_data); } - - wf_impl_jsonrpc_response_cleanup(&response); } diff --git a/lib/webfuse/adapter/impl/jsonrpc/server.h b/lib/webfuse/adapter/impl/jsonrpc/server.h index e5e53cb..51c827b 100644 --- a/lib/webfuse/adapter/impl/jsonrpc/server.h +++ b/lib/webfuse/adapter/impl/jsonrpc/server.h @@ -3,75 +3,46 @@ #ifndef __cplusplus #include -#include #include #else #include -#include -using std::size_t; #endif #include +#include "webfuse/core/status.h" +#include "webfuse/adapter/impl/jsonrpc/send_fn.h" #include "webfuse/adapter/impl/jsonrpc/method.h" -#include "webfuse/adapter/impl/time/timeout_manager.h" -#include "webfuse/adapter/impl/time/timer.h" #ifdef __cplusplus -extern "C" { -#endif - -struct wf_impl_jsonrpc_request +extern "C" { - bool is_pending; - wf_impl_jsonrpc_method_finished_fn * finished; - void * user_data; - int id; - struct wf_impl_timer timer; -}; +#endif struct wf_impl_jsonrpc_server { struct wf_impl_jsonrpc_method * methods; - struct wf_impl_jsonrpc_request request; }; extern void wf_impl_jsonrpc_server_init( - struct wf_impl_jsonrpc_server * server, - struct wf_impl_timeout_manager * manager); + struct wf_impl_jsonrpc_server * server); extern void wf_impl_jsonrpc_server_cleanup( struct wf_impl_jsonrpc_server * server); extern void wf_impl_jsonrpc_server_add( struct wf_impl_jsonrpc_server * server, - char const * name, + char const * method_name, wf_impl_jsonrpc_method_invoke_fn * invoke, - void * user_data ); - -extern void wf_impl_jsonrpc_server_invoke( - struct wf_impl_jsonrpc_server * server, - wf_impl_jsonrpc_method_finished_fn * finished, - void * user_data, - char const * method_name, - char const * param_info, - ... -); + void * user_data); -extern void wf_impl_jsonrpc_server_notify( - struct wf_impl_jsonrpc_server * server, - char const * method_name, - char const * param_info, - ... -); - -extern void wf_impl_jsonrpc_server_onresult( +extern void wf_impl_jsonrpc_server_process( struct wf_impl_jsonrpc_server * server, - char const * message, - size_t length); + json_t * request, + wf_impl_jsonrpc_send_fn * send, + void * user_data); #ifdef __cplusplus } #endif - #endif diff --git a/lib/webfuse/adapter/impl/operation/close.c b/lib/webfuse/adapter/impl/operation/close.c index fcf0645..6ccefc8 100644 --- a/lib/webfuse/adapter/impl/operation/close.c +++ b/lib/webfuse/adapter/impl/operation/close.c @@ -4,7 +4,7 @@ #include #include -#include "webfuse/adapter/impl/jsonrpc/server.h" +#include "webfuse/adapter/impl/jsonrpc/proxy.h" #include "webfuse/core/util.h" void wf_impl_operation_close( @@ -13,9 +13,13 @@ 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_server * rpc = user_data->rpc; + struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, inode); - int handle = (int) (file_info->fh & INT_MAX); - wf_impl_jsonrpc_server_notify(rpc, "close", "iii", inode, handle, file_info->flags); + if (NULL != rpc) + { + int handle = (int) (file_info->fh & INT_MAX); + wf_impl_jsonrpc_proxy_notify(rpc, "close", "iii", 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 6a3c359..98993e8 100644 --- a/lib/webfuse/adapter/impl/operation/getattr.c +++ b/lib/webfuse/adapter/impl/operation/getattr.c @@ -7,7 +7,7 @@ #include #include -#include "webfuse/adapter/impl/jsonrpc/server.h" +#include "webfuse/adapter/impl/jsonrpc/proxy.h" #include "webfuse/adapter/impl/jsonrpc/util.h" #include "webfuse/core/util.h" @@ -81,13 +81,20 @@ 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_server * rpc = user_data->rpc; + struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, inode); - struct wf_impl_operation_getattr_context * getattr_context = malloc(sizeof(struct wf_impl_operation_getattr_context)); - getattr_context->request = request; - getattr_context->uid = context->uid; - getattr_context->gid = context->gid; - getattr_context->timeout = user_data->timeout; + if (NULL != rpc) + { + struct wf_impl_operation_getattr_context * getattr_context = malloc(sizeof(struct wf_impl_operation_getattr_context)); + getattr_context->request = request; + getattr_context->uid = context->uid; + getattr_context->gid = context->gid; + getattr_context->timeout = user_data->timeout; - wf_impl_jsonrpc_server_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", "i", inode); + } + else + { + fuse_reply_err(request, ENOENT); + } } diff --git a/lib/webfuse/adapter/impl/operation/lookup.c b/lib/webfuse/adapter/impl/operation/lookup.c index 321be8a..840a754 100644 --- a/lib/webfuse/adapter/impl/operation/lookup.c +++ b/lib/webfuse/adapter/impl/operation/lookup.c @@ -10,7 +10,7 @@ #include -#include "webfuse/adapter/impl/jsonrpc/server.h" +#include "webfuse/adapter/impl/jsonrpc/proxy.h" #include "webfuse/adapter/impl/jsonrpc/util.h" #include "webfuse/core/util.h" @@ -90,13 +90,20 @@ 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_server * rpc = user_data->rpc; + struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, parent); - struct wf_impl_operation_lookup_context * lookup_context = malloc(sizeof(struct wf_impl_operation_lookup_context)); - lookup_context->request = request; - lookup_context->uid = context->uid; - lookup_context->gid = context->gid; - lookup_context->timeout = user_data->timeout; + if (NULL != rpc) + { + struct wf_impl_operation_lookup_context * lookup_context = malloc(sizeof(struct wf_impl_operation_lookup_context)); + lookup_context->request = request; + lookup_context->uid = context->uid; + lookup_context->gid = context->gid; + lookup_context->timeout = user_data->timeout; - wf_impl_jsonrpc_server_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", "is", (int) (parent & INT_MAX), name); + } + else + { + fuse_reply_err(request, ENOENT); + } } diff --git a/lib/webfuse/adapter/impl/operation/open.c b/lib/webfuse/adapter/impl/operation/open.c index b9e78d4..51894fa 100644 --- a/lib/webfuse/adapter/impl/operation/open.c +++ b/lib/webfuse/adapter/impl/operation/open.c @@ -4,7 +4,7 @@ #include #include -#include "webfuse/adapter/impl/jsonrpc/server.h" +#include "webfuse/adapter/impl/jsonrpc/proxy.h" #include "webfuse/core/util.h" #include "webfuse/core/status.h" @@ -47,7 +47,14 @@ 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_server * rpc = user_data->rpc; + struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, inode); - wf_impl_jsonrpc_server_invoke(rpc, &wf_impl_operation_open_finished, request, "open", "ii", inode, file_info->flags); + if (NULL != rpc) + { + wf_impl_jsonrpc_proxy_invoke(rpc, &wf_impl_operation_open_finished, request, "open", "ii", inode, file_info->flags); + } + else + { + fuse_reply_err(request, ENOENT); + } } diff --git a/lib/webfuse/adapter/impl/operation/read.c b/lib/webfuse/adapter/impl/operation/read.c index 7be1b01..8c956fb 100644 --- a/lib/webfuse/adapter/impl/operation/read.c +++ b/lib/webfuse/adapter/impl/operation/read.c @@ -6,7 +6,7 @@ #include #include -#include "webfuse/adapter/impl/jsonrpc/server.h" +#include "webfuse/adapter/impl/jsonrpc/proxy.h" #define WF_MAX_READ_LENGTH 4096 @@ -87,9 +87,16 @@ 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_server * rpc = user_data->rpc; + struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, inode); - int const length = (size <= WF_MAX_READ_LENGTH) ? (int) size : WF_MAX_READ_LENGTH; - int handle = (file_info->fh & INT_MAX); - wf_impl_jsonrpc_server_invoke(rpc, &wf_impl_operation_read_finished, request, "read", "iiii", inode, handle, (int) offset, length); + 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); + } + else + { + fuse_reply_err(request, ENOENT); + } } diff --git a/lib/webfuse/adapter/impl/operation/readdir.c b/lib/webfuse/adapter/impl/operation/readdir.c index 8c027ee..104e807 100644 --- a/lib/webfuse/adapter/impl/operation/readdir.c +++ b/lib/webfuse/adapter/impl/operation/readdir.c @@ -8,7 +8,7 @@ #include #include -#include "webfuse/adapter/impl/jsonrpc/server.h" +#include "webfuse/adapter/impl/jsonrpc/proxy.h" #include "webfuse/core/util.h" @@ -137,11 +137,19 @@ 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_server * rpc = user_data->rpc; + struct wf_impl_jsonrpc_proxy * rpc = wf_impl_operations_context_get_proxy(user_data, inode); + + if (NULL != rpc) + { struct wf_impl_operation_readdir_context * readdir_context = malloc(sizeof(struct wf_impl_operation_readdir_context)); readdir_context->request = request; readdir_context->size = size; readdir_context->offset = offset; - wf_impl_jsonrpc_server_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", "i", inode); + } + else + { + fuse_reply_err(request, ENOENT); + } } diff --git a/lib/webfuse/adapter/impl/operations.c b/lib/webfuse/adapter/impl/operations.c new file mode 100644 index 0000000..75949b5 --- /dev/null +++ b/lib/webfuse/adapter/impl/operations.c @@ -0,0 +1,20 @@ +#include "webfuse/adapter/impl/operations.h" +#include "webfuse/adapter/impl/session_manager.h" +#include "webfuse/adapter/impl/session.h" +#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_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); + if (NULL != session) + { + proxy = &session->rpc; + } + + return proxy; +} diff --git a/lib/webfuse/adapter/impl/operations.h b/lib/webfuse/adapter/impl/operations.h index 74875ae..fe1a540 100644 --- a/lib/webfuse/adapter/impl/operations.h +++ b/lib/webfuse/adapter/impl/operations.h @@ -7,11 +7,12 @@ extern "C" { #endif -struct wf_impl_jsonrpc_server; +struct wf_impl_session_manager; +struct wf_impl_jsonrpc_proxy; struct wf_impl_operations_context { - struct wf_impl_jsonrpc_server * rpc; + struct wf_impl_session_manager * session_manager; double timeout; }; @@ -47,6 +48,10 @@ extern void wf_impl_operation_read( fuse_ino_t ino, size_t size, off_t off, 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); + #ifdef __cplusplus } #endif diff --git a/lib/webfuse/adapter/impl/server_protocol.c b/lib/webfuse/adapter/impl/server_protocol.c index 2d817ab..0e89437 100644 --- a/lib/webfuse/adapter/impl/server_protocol.c +++ b/lib/webfuse/adapter/impl/server_protocol.c @@ -7,6 +7,8 @@ #include "webfuse/core/util.h" #include "webfuse/adapter/impl/filesystem.h" +#include "webfuse/adapter/impl/credentials.h" +#include "webfuse/adapter/impl/jsonrpc/request.h" static int wf_impl_server_protocol_callback( struct lws * wsi, @@ -38,7 +40,8 @@ static int wf_impl_server_protocol_callback( &protocol->session_manager, wsi, &protocol->authenticators, - &protocol->rpc); + &protocol->timeout_manager, + &protocol->server); if (NULL != session) { @@ -70,20 +73,6 @@ static int wf_impl_server_protocol_callback( return 0; } -static bool wf_impl_server_protocol_invoke( - void * user_data, - json_t const * request) -{ - struct wf_server_protocol * protocol = user_data; - struct wf_impl_session * session = &protocol->session_manager.session; - struct wf_message * message = wf_message_create(request); - - bool const result = wf_impl_session_send(session, message); - - return result; -} - - struct wf_server_protocol * wf_impl_server_protocol_create( char * mount_point) { @@ -116,6 +105,41 @@ void wf_impl_server_protocol_init_lws( lws_protocol->user = protocol; } +static void wf_impl_server_protocol_authenticate( + struct wf_impl_jsonrpc_request * request, + char const * WF_UNUSED_PARAM(method_name), + json_t * params, + void * WF_UNUSED_PARAM(user_data)) +{ + bool result = false; + + json_t * type_holder = json_array_get(params, 0); + json_t * creds_holder = json_array_get(params, 1); + + if (json_is_string(type_holder) && json_is_object(creds_holder)) + { + char const * type = json_string_value(type_holder); + struct wf_credentials creds; + + wf_impl_credentials_init(&creds, type, creds_holder); + struct wf_impl_session * session = wf_impl_jsonrpc_request_get_userdata(request); + result = wf_impl_session_authenticate(session, &creds); + + wf_impl_credentials_cleanup(&creds); + } + + + if (result) + { + json_t * result = json_object(); + wf_impl_jsonrpc_respond(request, result); + } + else + { + wf_impl_jsonrpc_respond_error(request, WF_BAD_ACCESS_DENIED); + } +} + bool wf_impl_server_protocol_init( struct wf_server_protocol * protocol, char * mount_point) @@ -124,20 +148,15 @@ bool wf_impl_server_protocol_init( wf_impl_session_manager_init(&protocol->session_manager); wf_impl_authenticators_init(&protocol->authenticators); - wf_impl_jsonrpc_server_init(&protocol->rpc, &protocol->timeout_manager); - wf_impl_jsonrpc_server_add(&protocol->rpc, "lookup", &wf_impl_server_protocol_invoke, protocol); - wf_impl_jsonrpc_server_add(&protocol->rpc, "getattr", &wf_impl_server_protocol_invoke, protocol); - wf_impl_jsonrpc_server_add(&protocol->rpc, "readdir", &wf_impl_server_protocol_invoke, protocol); - wf_impl_jsonrpc_server_add(&protocol->rpc, "open", &wf_impl_server_protocol_invoke, protocol); - wf_impl_jsonrpc_server_add(&protocol->rpc, "close", &wf_impl_server_protocol_invoke, protocol); - wf_impl_jsonrpc_server_add(&protocol->rpc, "read", &wf_impl_server_protocol_invoke, protocol); + 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->rpc, mount_point); + 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->rpc); + 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); @@ -150,7 +169,7 @@ void wf_impl_server_protocol_cleanup( struct wf_server_protocol * protocol) { wf_impl_filesystem_cleanup(&protocol->filesystem); - wf_impl_jsonrpc_server_cleanup(&protocol->rpc); + wf_impl_jsonrpc_server_cleanup(&protocol->server); wf_impl_timeout_manager_cleanup(&protocol->timeout_manager); wf_impl_authenticators_cleanup(&protocol->authenticators); wf_impl_session_manager_cleanup(&protocol->session_manager); diff --git a/lib/webfuse/adapter/impl/server_protocol.h b/lib/webfuse/adapter/impl/server_protocol.h index 599f9a5..8a4b0d7 100644 --- a/lib/webfuse/adapter/impl/server_protocol.h +++ b/lib/webfuse/adapter/impl/server_protocol.h @@ -2,10 +2,11 @@ #define WF_ADAPTER_IMPL_SERVER_PROTOCOL_H #include "webfuse/adapter/impl/filesystem.h" -#include "webfuse/adapter/impl/jsonrpc/server.h" +#include "webfuse/adapter/impl/jsonrpc/proxy.h" #include "webfuse/adapter/impl/time/timeout_manager.h" #include "webfuse/adapter/impl/authenticators.h" #include "webfuse/adapter/impl/session_manager.h" +#include "webfuse/adapter/impl/jsonrpc/server.h" #ifdef __cplusplus extern "C" @@ -18,9 +19,9 @@ struct wf_server_protocol { struct wf_impl_timeout_manager timeout_manager; struct wf_impl_filesystem filesystem; - struct wf_impl_jsonrpc_server rpc; 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( diff --git a/lib/webfuse/adapter/impl/session.c b/lib/webfuse/adapter/impl/session.c index 101a950..b45027d 100644 --- a/lib/webfuse/adapter/impl/session.c +++ b/lib/webfuse/adapter/impl/session.c @@ -2,60 +2,70 @@ #include "webfuse/adapter/impl/authenticators.h" #include "webfuse/core/message_queue.h" #include "webfuse/core/message.h" -#include "webfuse/adapter/impl/jsonrpc/server.h" +#include "webfuse/adapter/impl/jsonrpc/proxy.h" +#include "webfuse/adapter/impl/jsonrpc/request.h" +#include "webfuse/adapter/impl/jsonrpc/response.h" #include #include +static bool wf_impl_session_send( + json_t * request, + void * user_data) +{ + struct wf_impl_session * session = user_data; + struct wf_message * message = wf_message_create(request); + + bool result = (session->is_authenticated || wf_impl_jsonrpc_is_response(request)) && (NULL != session->wsi); + + if (result) + { + wf_message_queue_push(&session->queue, message); + lws_callback_on_writable(session->wsi); + + result = true; + } + else + { + wf_message_dispose(message); + } + + return result; +} + void wf_impl_session_init( struct wf_impl_session * session, struct lws * wsi, struct wf_impl_authenticators * authenticators, - struct wf_impl_jsonrpc_server * rpc) + struct wf_impl_timeout_manager * timeout_manager, + struct wf_impl_jsonrpc_server * server) { session->wsi = wsi; session->is_authenticated = false; session->authenticators = authenticators; - session->rpc = rpc; + 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) { + wf_impl_jsonrpc_proxy_cleanup(&session->rpc); wf_message_queue_cleanup(&session->queue); session->is_authenticated = false; session->wsi = NULL; session->authenticators = NULL; - session->rpc = NULL; + session->server = NULL; } -void wf_impl_session_authenticate( +bool wf_impl_session_authenticate( struct wf_impl_session * session, struct wf_credentials * creds) { session->is_authenticated = wf_impl_authenticators_authenticate(session->authenticators, creds); -} - -bool wf_impl_session_send( - struct wf_impl_session * session, - struct wf_message * message) -{ - bool result = (session->is_authenticated) && (NULL != session->wsi); - if (result) - { - wf_message_queue_push(&session->queue, message); - lws_callback_on_writable(session->wsi); - - result = true; - } - else - { - wf_message_dispose(message); - } - - return result; + return session->is_authenticated; } void wf_impl_session_onwritable( @@ -80,5 +90,19 @@ void wf_impl_session_receive( char const * data, size_t length) { - wf_impl_jsonrpc_server_onresult(session->rpc, data, length); + json_t * message = json_loadb(data, length, 0, NULL); + if (NULL != message) + { + if (wf_impl_jsonrpc_is_response(message)) + { + wf_impl_jsonrpc_proxy_onresult(&session->rpc, message); + } + else if (wf_impl_jsonrpc_is_request(message)) + { + wf_impl_jsonrpc_server_process(session->server, message, &wf_impl_session_send, session); + } + + json_decref(message); + } + } \ No newline at end of file diff --git a/lib/webfuse/adapter/impl/session.h b/lib/webfuse/adapter/impl/session.h index 598c551..116e196 100644 --- a/lib/webfuse/adapter/impl/session.h +++ b/lib/webfuse/adapter/impl/session.h @@ -10,6 +10,8 @@ using std::size_t; #endif #include "webfuse/core/message_queue.h" +#include "webfuse/adapter/impl/jsonrpc/proxy.h" +#include "webfuse/adapter/impl/jsonrpc/server.h" #ifdef __cplusplus extern "C" @@ -20,7 +22,7 @@ struct lws; struct wf_message; struct wf_credentials; struct wf_impl_authenticators; -struct wf_impl_jsonrpc_server; +struct wf_impl_timeout_manager; struct wf_impl_session { @@ -28,23 +30,21 @@ struct wf_impl_session bool is_authenticated; struct wf_message_queue queue; struct wf_impl_authenticators * authenticators; - struct wf_impl_jsonrpc_server * rpc; + struct wf_impl_jsonrpc_server * server; + struct wf_impl_jsonrpc_proxy rpc; }; extern void wf_impl_session_init( struct wf_impl_session * session, struct lws * wsi, struct wf_impl_authenticators * authenticators, - struct wf_impl_jsonrpc_server * rpc); + struct wf_impl_timeout_manager * timeout_manager, + struct wf_impl_jsonrpc_server * server); -extern void wf_impl_session_authenticate( +extern bool wf_impl_session_authenticate( struct wf_impl_session * session, struct wf_credentials * creds); -extern bool wf_impl_session_send( - struct wf_impl_session * session, - struct wf_message * message); - extern void wf_impl_session_receive( struct wf_impl_session * session, char const * data, diff --git a/lib/webfuse/adapter/impl/session_manager.c b/lib/webfuse/adapter/impl/session_manager.c index e1f61c2..be87c2a 100644 --- a/lib/webfuse/adapter/impl/session_manager.c +++ b/lib/webfuse/adapter/impl/session_manager.c @@ -1,10 +1,11 @@ #include "webfuse/adapter/impl/session_manager.h" +#include "webfuse/core/util.h" #include void wf_impl_session_manager_init( struct wf_impl_session_manager * manager) { - wf_impl_session_init(&manager->session, NULL, NULL, NULL); + wf_impl_session_init(&manager->session, NULL, NULL, NULL, NULL); } void wf_impl_session_manager_cleanup( @@ -17,13 +18,14 @@ struct wf_impl_session * wf_impl_session_manager_add( struct wf_impl_session_manager * manager, struct lws * wsi, struct wf_impl_authenticators * authenticators, - struct wf_impl_jsonrpc_server * rpc) + struct wf_impl_timeout_manager * timeout_manager, + struct wf_impl_jsonrpc_server * server) { struct wf_impl_session * session = NULL; if (NULL == manager->session.wsi) { session = &manager->session; - wf_impl_session_init(&manager->session, wsi, authenticators, rpc); + wf_impl_session_init(&manager->session, wsi, authenticators, timeout_manager, server); } return session; @@ -42,6 +44,22 @@ struct wf_impl_session * wf_impl_session_manager_get( return session; } +struct wf_impl_session * wf_impl_session_manager_get_by_inode( + struct wf_impl_session_manager * manager, + fuse_ino_t WF_UNUSED_PARAM(inode)) +{ + // ToDo: use inode to determine session manager + + struct wf_impl_session * session = NULL; + if (NULL != manager->session.wsi) + { + session = &manager->session; + } + + return session; +} + + void wf_impl_session_manager_remove( struct wf_impl_session_manager * manager, struct lws * wsi) diff --git a/lib/webfuse/adapter/impl/session_manager.h b/lib/webfuse/adapter/impl/session_manager.h index 562d078..6b7a26b 100644 --- a/lib/webfuse/adapter/impl/session_manager.h +++ b/lib/webfuse/adapter/impl/session_manager.h @@ -6,6 +6,7 @@ #endif #include "webfuse/adapter/impl/session.h" +#include "webfuse/adapter/impl/fuse_wrapper.h" #ifdef __cplusplus extern "C" @@ -13,6 +14,8 @@ extern "C" #endif struct lws; +struct wf_impl_timeout_manager; +struct wf_impl_jsonrpc_server; struct wf_impl_session_manager { @@ -29,12 +32,17 @@ extern struct wf_impl_session * wf_impl_session_manager_add( struct wf_impl_session_manager * manager, struct lws * wsi, struct wf_impl_authenticators * authenticators, - struct wf_impl_jsonrpc_server * rpc); + struct wf_impl_timeout_manager * timeout_manager, + struct wf_impl_jsonrpc_server * server); 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/status.c b/lib/webfuse/core/status.c index c7ea0de..9eb9964 100644 --- a/lib/webfuse/core/status.c +++ b/lib/webfuse/core/status.c @@ -12,7 +12,7 @@ case WF_BAD_BUSY: return -ENOENT; case WF_BAD_FORMAT: return -ENOENT; case WF_BAD_NOENTRY: return -ENOENT; - case WF_BAD_NOACCESS: return -EACCES; + case WF_BAD_ACCESS_DENIED: return -EACCES; default: return -ENOENT; } } @@ -28,7 +28,7 @@ char const * wf_status_tostring(wf_status status) case WF_BAD_BUSY: return "Bad (busy)"; case WF_BAD_FORMAT: return "Bad (format)"; case WF_BAD_NOENTRY: return "Bad (no entry)"; - case WF_BAD_NOACCESS: return "Bad (no access)"; + case WF_BAD_ACCESS_DENIED: return "Bad (access denied)"; default: return "Bad (unknown)"; } } diff --git a/test/test_response_parser.cc b/test/test_response_parser.cc index bacb7a9..db7fe2f 100644 --- a/test/test_response_parser.cc +++ b/test/test_response_parser.cc @@ -8,25 +8,18 @@ static void response_parse_str( std::string const & buffer, struct wf_impl_jsonrpc_response * response) { - wf_impl_jsonrpc_response_init(response, buffer.c_str(), buffer.size()); + json_t * message = json_loadb(buffer.c_str(), buffer.size(), 0, nullptr); + if (nullptr != message) + { + wf_impl_jsonrpc_response_init(response, message); + json_decref(message); + } } TEST(response_parser, test) { struct wf_impl_jsonrpc_response response; - // invalid json - response_parse_str("", &response); - ASSERT_NE(WF_GOOD, response.status); - ASSERT_EQ(-1, response.id); - ASSERT_EQ(nullptr, response.result); - - // invalid json - response_parse_str("invalid_json", &response); - ASSERT_NE(WF_GOOD, response.status); - ASSERT_EQ(-1, response.id); - ASSERT_EQ(nullptr, response.result); - // no object response_parse_str("[]", &response); ASSERT_NE(WF_GOOD, response.status);