Serving Files and Directories ----------------------------- Build with ``UA_BUILD_EXAMPLES``, Method calls and a reduced or full Namespace Zero. This example serves a local directory and a separate read-only file through the OPC UA Part 20 Methods. Create a backend, construct its driver, then register it with the server. Registration starts the driver if the server is already running; otherwise it starts with the server. Shutdown stops drivers; deleting the server frees them. For parameters and backend callbacks, see :doc:`driver_file_transfer`. A custom in-memory backend is shown in ``examples/filetransfer/server_filetransfer_custom_backend.c``. Serving a local directory ^^^^^^^^^^^^^^^^^^^^^^^^^ The default root Object is named "FileSystem". Its entries are mirrored on start and refreshed periodically. Client Methods operate on storage directly. .. code-block:: c static UA_StatusCode mountLocalDirectory(UA_Server *server, const char *rootPath) { UA_FileTransferBackend backend; UA_StatusCode res = UA_FileTransferBackend_localDirectory(UA_STRING((char*)(uintptr_t)rootPath), &backend); if(res != UA_STATUSCODE_GOOD) return res; UA_Driver *ftd = NULL; res = UA_FileTransferDriver_newDirectory(server, &backend, NULL, NULL, &ftd); if(res != UA_STATUSCODE_GOOD) { backend.file.clear(&backend.file); return res; } res = UA_Server_addDriver(server, ftd); if(res != UA_STATUSCODE_GOOD) ftd->free(ftd); return res; } Exposing a single file ^^^^^^^^^^^^^^^^^^^^^^ Individual files (a configuration file, a manual, a log) can be exposed as standalone FileType Objects without mirroring a whole directory. Here the file is served read-only: the Writable/UserWritable Properties are false and opening the file for writing fails. .. code-block:: c static UA_StatusCode addReadOnlyFile(UA_Server *server, const char *rootPath) { char filePath[512]; int length = snprintf(filePath, sizeof(filePath), "%s/readme.txt", rootPath); if(length < 0 || (size_t)length >= sizeof(filePath)) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_FileTransferFileBackend backend; UA_StatusCode res = UA_FileTransferFileBackend_localFile(UA_STRING(filePath), &backend); if(res != UA_STATUSCODE_GOOD) return res; UA_Driver *ftd = NULL; UA_FileTransferNodeDescription description; memset(&description, 0, sizeof(description)); description.browseName = UA_QUALIFIEDNAME(1, "ReadMe"); res = UA_FileTransferDriver_newFile(server, &backend, UA_STRING_NULL, &description, NULL, &ftd); if(res != UA_STATUSCODE_GOOD) { backend.clear(&backend); return res; } /* Configure the driver before adding it: registration starts it immediately * when the server is already running. */ UA_Boolean readOnly = true; res = UA_KeyValueMap_setScalar(&ftd->params, UA_QUALIFIEDNAME(0, "read-only"), &readOnly, &UA_TYPES[UA_TYPES_BOOLEAN]); if(res == UA_STATUSCODE_GOOD) res = UA_Server_addDriver(server, ftd); if(res != UA_STATUSCODE_GOOD) ftd->free(ftd); return res; } /* Prepare the served directory with demo content */ static void prepareDemoDirectory(const char *rootPath) {