KasperskyOS Community Edition 1.1
[Topic sc_filesystems_and_net]

Contents of the VFS component

The VFS component contains a set of executable files, libraries and description files that let you use file systems and/or a network stack combined into a separate Virtual File System (VFS) process. If necessary, you can build your own VFS implementations.

VFS libraries

The vfs CMake package contains the following libraries:

  • vfs_fs – contains the defvs, ramfs and romfs implementations, and lets you add implementations of other file systems to VFS.
  • vfs_net – contains the defvs implementation and network stack.
  • vfs_imp – contains the sum of the vfs_fs and vfs_net components.
  • vfs_remote – client transport library that converts local calls into IPC requests to VFS and receives IPC responses.
  • vfs_server – server transport library of VFS that receives IPC requests, converts them into local calls, and sends IPC responses.
  • vfs_local – used for statically linking the client to VFS libraries.

VFS executable files

The precompiled_vfs CMake package contains the following executable files:

  • VfsRamFs
  • VfsSdCardFs
  • VfsNet

The VfsRamFs and VfsSdCardFs executable files include the vfs_server, vfs_fs, vfat and lwext4 libraries. The VfsNet executable file includes the vfs_server, vfs_imp and dnet_imp libraries.

Each of these executable files have their own default values for arguments and environment variables.

If necessary, you can independently build a VFS executable file with the necessary functionality.

VFS description files

The directory /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/kl/ contains the following VFS files:

  • VfsRamFs.edl, VfsSdCardFs.edl, VfsNet.edl and VfsEntity.edl, and the header files generated from them, including the transport code.
  • Vfs.cdl and the generated Vfs.cdl.h.
  • Vfs*.idl and the header files generated from them, including the transport code.

Page top

[Topic vfs_overview]

Creating an IPC channel to VFS

Let's examine a Client program using file systems and Berkeley sockets. To handle its calls, we start one VFS process (named VfsFsnet). Network calls and file calls will be sent to this process. This approach is utilized when there is no need to separate file data streams from network data streams.

To ensure correct interaction between the Client and VfsFsnet processes, the name of the IPC channel between them must be defined by the _VFS_CONNECTION_ID macro declared in the vfs/defs.h file.

Below is a fragment of an init description for connecting the Client and VfsFsnet processes.

init.yaml

- name: Client

connections:

- target: VfsFsnet

id: {var: _VFS_CONNECTION_ID, include: vfs/defs.h}

- name: VfsFsnet

Page top
[Topic client_and_vfs_ipc_channel]

Building a VFS executable file

When building a VFS executable file, you can include whatever specific functionality is required in this file, such as:

  • Implementation of a specific file system
  • Network stack
  • Network driver

For example, you will need to build a "file version" and a "network version" of VFS to separate file calls from network calls. In some cases, you will need to include a network stack and file systems in the VFS ("full version" of VFS).

Building a "file version" of VFS

Let's examine a VFS program containing only an implementation of the lwext4 file system without a network stack. To build this executable file, the file containing the main() function must be linked to the vfs_server, vfs_fs and lwext4 libraries:

CMakeLists.txt

project (vfsfs)

include (platform/nk)

# Set compile flags

project_header_default ("STANDARD_GNU_11:YES" "STRICT_WARNINGS:NO")

add_executable (VfsFs "src/vfs.c")

# Linking with VFS libraries

target_link_libraries (VfsFs

${vfs_SERVER_LIB}

${LWEXT4_LIB}

${vfs_FS_LIB})

# Prepare VFS to connect to the ramdisk driver process

set_target_properties (VfsFs PROPERTIES ${blkdev_ENTITY}_REPLACEMENT ${ramdisk_ENTITY})

A block device driver cannot be linked to VFS and therefore must also be run as a separate process.

Interaction between three processes: client, "file version" of VFS, and block device driver.

Building a "network version" of VFS together with a network driver

Let's examine a VFS program containing a network stack with a driver but without implementations of files systems. To build this executable file, the file containing the main() function must be linked to the vfs_server, vfs_implementation and dnet_implementation libraries.

CMakeLists.txt

project (vfsnet)

include (platform/nk)

# Set compile flags

project_header_default ("STANDARD_GNU_11:YES" "STRICT_WARNINGS:NO")

add_executable (VfsNet "src/vfs.c")

# Linking with VFS libraries

target_link_libraries (VfsNet

${vfs_SERVER_LIB}

${vfs_IMPLEMENTATION_LIB}

${dnet_IMPLEMENTATION_LIB})

# Disconnect the block device driver

set_target_properties (VfsNet PROPERTIES ${blkdev_ENTITY}_REPLACEMENT "")

The dnet_implementation library already includes a network driver, therefore it is not necessary to start a separate driver process.

Interaction between the Client process and the process of the "network version" of VFS

Building a "network version" of VFS with a separate network driver

Another option is to build the "network version" of VFS without a network driver. The network driver will need to be started as a separate process. Interaction with the driver occurs via IPC using the dnet_client library.

In this case, the file containing the main() function must be linked to the vfs_server, vfs_implementation and dnet_client libraries.

CMakeLists.txt

project (vfsnet)

include (platform/nk)

# Set compile flags

project_header_default ("STANDARD_GNU_11:YES" "STRICT_WARNINGS:NO")

add_executable (VfsNet "src/vfs.c")

# Linking with VFS libraries

target_link_libraries (VfsNet

${vfs_SERVER_LIB}

${vfs_IMPLEMENTATION_LIB}

${dnet_CLIENT_LIB})

# Disconnect the block device driver

set_target_properties (VfsNet PROPERTIES ${blkdev_ENTITY}_REPLACEMENT "")

Interaction between three processes: client, "network version" of VFS, and network driver.

Building a "full version" of VFS

If the VFS needs to include a network stack and implementations of file systems, the build should use the vfs_server library, vfs_implementation library, dnet_implementation library (or dnet_client library for a separate network driver), and the libraries for implementing file systems.

Page top

[Topic vfs_app_build]

Merging a client and VFS into one executable file

Let's examine a Client program using Berkeley sockets. Calls made by the Client must be sent to VFS. The normal path consists of starting a separate VFS process and creating an IPC channel. Alternatively, you can integrate VFS functionality directly into the Client executable file. To do so, when building the Client executable file, you need to link it to the vfs_local library that will receive calls, and link it to the implementation libraries vfs_implementation and dnet_implementation.

Local linking with VFS is convenient during debugging. In addition, calls for working with the network can be handled much faster due to the exclusion of IPC calls. Nevertheless, insulation of the VFS in a separate process and IPC interaction with it is always recommended as a more secure approach.

Below is a build script for the Client executable file.

CMakeLists.txt

project (client)

include (platform/nk)

# Set compile flags

project_header_default ("STANDARD_GNU_11:YES" "STRICT_WARNINGS:NO")

# Generates the Client.edl.h file

nk_build_edl_files (client_edl_files NK_MODULE "client" EDL "${CMAKE_SOURCE_DIR}/resources/edl/Client.edl")

add_executable (Client "src/client.c")

add_dependencies (Client client_edl_files)

# Linking with VFS libraries

target_link_libraries (Client ${vfs_LOCAL_LIB} ${vfs_IMPLEMENTATION_LIB} ${dnet_IMPLEMENTATION_LIB}

If the Client uses file systems, it must also be linked to the vfs_fs library and to the implementation of the utilized file system in addition to its linking to vfs_local. You also need to add a block device driver to the solution.

Page top
[Topic client_and_vfs_linked]

Overview: arguments and environment variables of VFS

VFS arguments

  • -l <entry in fstab format>

    The -l argument lets you mount the file system.

  • -f <path to fstab file>

    The -f argument lets you pass the file containing entries in fstab format for mounting file systems. The ROMFS storage will be searched for the file. If the UMNAP_ROMFS variable is defined, the file system mounted using the ROOTFS variable will be searched for the file.

Example of using the -l and -f arguments

VFS environment variables

  • UNMAP_ROMFS

    If the UNMAP_ROMFS variable is defined, the ROMFS storage will be deleted. This helps conserve memory and change behavior when using the -f argument.

  • ROOTFS = <entry in fstab format>

    The ROOTFS variable lets you mount a file system to the root directory. In combination with the UNMAP_ROMFS variable and the -f argument, it lets you search for the fstab file in the mounted file system instead of in the ROMFS storage. ROOTFS usage example

  • VFS_CLIENT_MAX_THREADS

    The VFS_CLIENT_MAX_THREADS environment variable lets you redefine the SDK configuration parameter VFS_CLIENT_MAX_THREADS during VFS startup.

  • _VFS_NETWORK_BACKEND=<backend name>:<name of the IPC channel to VFS>

The _VFS_NETWORK_BACKEND variable defines the backend used for network calls. You can specify the name of a standard backend such as client, server or local, and the name of a custom backend. If the local backend is used, the name of the IPC channel is not specified (_VFS_NETWORK_BACKEND=local:). You can specify two or more IPC channels by separating them with a comma.

  • _VFS_FILESYSTEM_BACKEND=<backend name>:<name of the IPC channel to VFS>

    The _VFS_FILESYSTEM_BACKEND variable defines the backend used for file calls. The backend name and name of the IPC channel to VFS are defined the same as way as they were for the _VFS_NETWORK_BACKEND variable.

Default values

For the VfsRamFs executable file:

ROOTFS = ramdisk0,0 / ext4 0

VFS_FILESYSTEM_BACKEND = server:kl.VfsRamFs

For the VfsSdCardFs executable file:

ROOTFS = mmc0,0 / fat32 0

VFS_FILESYSTEM_BACKEND = server:kl.VfsSdCardFs

-l nodev /tmp ramfs 0

-l nodev /var ramfs 0

For the VfsNet executable file:

VFS_NETWORK_BACKEND = server:kl.VfsNet

VFS_FILESYSTEM_BACKEND = server:kl.VfsNet

-l devfs /dev devfs 0

Page top
[Topic vfs_args_and_envs_overview]

Mounting a file system at startup

When the VFS process starts, only the RAMFS file system is mounted to the root directory by default. If you need to mount other file systems, this can be done not only by using the mount() call after the VFS starts but can also be done immediately when the VFS process starts by passing the necessary arguments and environment variables to it.

Let's examine three examples of mounting file systems at VFS startup. The Env program is used to pass arguments and environment variables to the VFS process.

Mounting with the -l argument

A simple way to mount a file system is to pass the -l <entry in fstab format> argument to the VFS process.

In this example, the devfs and romfs file systems will be mounted when the process named Vfs1 is started.

env.c

#include <env/env.h>

#include <stdlib.h>

int main(int argc, char** argv)

{

const char* Vfs1Args[] = {

"-l", "devfs /dev devfs 0",

"-l", "romfs /etc romfs 0"

};

ENV_REGISTER_ARGS("Vfs1", Vfs1Args);

envServerRun();

return EXIT_SUCCESS;

}

Mounting with fstab from ROMFS

If an fstab file is added when building a solution, the file will be available through the ROMFS storage after startup. It can be used for mounting by passing the -f <path to fstab file> argument to the VFS process.

In this example, the file systems defined via the fstab file that was added during the solution build will be mounted when the process named Vfs2 is started.

env.c

#include <env/env.h>

#include <stdlib.h>

int main(int argc, char** argv)

{

const char* Vfs2Args[] = { "-f", "fstab" };

ENV_REGISTER_ARGS("Vfs2", Vfs2Args);

envServerRun();

return EXIT_SUCCESS;

}

Mounting with an external fstab

Let's assume that the fstab file is located on a drive and not in the ROMFS image of the solution. To use it for mounting, you need to pass the following arguments and environment variables to VFS:

  1. ROOTFS. This variable lets you mount the file system containing the fstab file into the root directory.
  2. UNMAP_ROMFS. If this variable is defined, the ROMFS storage is deleted. As a result, the fstab file will be sought in the file system mounted using the ROOTFS variable.
  3. -f. This argument is used to define the path to the fstab file.

In the next example, the ext2 file system containing the /etc/fstab file used for mounting additional file systems will be mounted to the root directory when the process named Vfs3 starts. The ROMFS storage will be deleted.

env.c

#include <env/env.h>

#include <stdlib.h>

int main(int argc, char** argv)

{

const char* Vfs3Args[] = { "-f", "/etc/fstab" };

const char* Vfs3Envs[] = {

"ROOTFS=ramdisk0,0 / ext2 0",

"UNMAP_ROMFS=1"

};

ENV_REGISTER_PROGRAM_ENVIRONMENT("Vfs3", Vfs3Args, Vfs3Envs);

envServerRun();

return EXIT_SUCCESS;

}

Page top
[Topic mount_on_start]

Using VFS backends to separate file calls and network calls

This example shows a secure development pattern that separates network data streams from file data streams.

Let's examine a Client program using file systems and Berkeley sockets. To handle its calls, we will start not one but two separate VFS processes from the VfsFirst and VfsSecond executable files. We will use environment variables to assign the file backends to work via the channel to VfsFirst and assign the network backends to work via the channel to VfsSecond. We will use the standard backends client and server. This way, we will redirect the file calls of the Client to VfsFirst and redirect the network calls to VfsSecond. To pass the environment variables to processes, we will add the Env program to the solution.

The init description of the solution is provided below. The Client process will be connected to the VfsFirst and VfsSecond processes, and each of the three processes will be connected to the Env process. Please note that the name of the IPC channel to the Env process is defined by using the ENV_SERVICE_NAME variable.

init.yaml

entities:

- name: Env

- name: Client

connections:

- target: Env

id: {var: ENV_SERVICE_NAME, include: env/env.h}

- target: VfsFirst

id: VFS1

- target: VfsSecond

id: VFS2

- name: VfsFirst

connections:

- target: Env

id: {var: ENV_SERVICE_NAME, include: env/env.h}

- name: VfsSecond

connections:

- target: Env

id: {var: ENV_SERVICE_NAME, include: env/env.h}

To send all file calls to VfsFirst, we define the value of the _VFS_FILESYSTEM_BACKEND environment variable as follows:

  • For VfsFirst: _VFS_FILESYSTEM_BACKEND=server:<name of the IPC channel to VfsFirst>
  • For Client: _VFS_FILESYSTEM_BACKEND=client:<name of the IPC channel to VfsFirst>

To send network calls to VfsSecond, we use the equivalent _VFS_NETWORK_BACKEND environment variable:

  • We define the following for VfsSecond: _VFS_NETWORK_BACKEND=server:<name of the IPC channel to the VfsSecond>
  • We define the following for the Client: _VFS_NETWORK_BACKEND=client: <name of the IPC channel to the VfsSecond>

We define the value of environment variables through the Env program, which is presented below.

env.c

#include <env/env.h>

#include <stdlib.h>

int main(void)

{

const char* vfs_first_envs[] = { "_VFS_FILESYSTEM_BACKEND=server:VFS1" };

ENV_REGISTER_VARS("VfsFirst", vfs_first_envs);

const char* vfs_second_envs[] = { "_VFS_NETWORK_BACKEND=server:VFS2" };

ENV_REGISTER_VARS("VfsSecond", vfs_second_envs);

const char* client_envs[] = { "_VFS_FILESYSTEM_BACKEND=client:VFS1", "_VFS_NETWORK_BACKEND=client:VFS2" };

ENV_REGISTER_VARS("Client", client_envs);

envServerRun();

return EXIT_SUCCESS;

}

Page top
[Topic client_and_two_vfs]

Writing a custom VFS backend

This example shows how to change the logic for handling file calls using a special VFS backend.

Let's examine a solution that includes the Client, VfsFirst and VfsSecond processes. Let's assume that the Client process is connected to VfsFirst and VfsSecond using IPC channels.

Our task is to ensure that queries from the Client process to the fat32 file system are handled by the VfsFirst process, and queries to the ext4 file system are handled by the VfsSecond process. To accomplish this task, we can use the VFS backend mechanism and will not even need to change the code of the Client program.

We will write a custom backend named custom_client, which will send calls via the VFS1 or VFS2 channel depending on whether or not the file path begins with /mnt1. To send calls, custom_client will use the standard backends of the client. In other words, it will act as a proxy backend.

We use the -l argument to mount fat32 to the /mnt1 directory for the VfsFirst process and mount ext4 to /mnt2 for the VfsSecond process. (It is assumed that VfsFirst contains a fat32 implementation and VfsSecond contains an ext4 implementation.) We use the _VFS_FILESYSTEM_BACKEND environment variable to define the backends (custom_client and server) and IPC channels (VFS1 and VFS2) to be used by the processes.

Then we use the init description to define the names of the IPC channels: VFS1 and VFS2.

This is examined in more detail below:

  1. Code of the custom_client backend.
  2. Linking of the Client program and the custom_client backend.
  3. Env program code.
  4. Init description.

Writing a custom_client backend

This file contains an implementation of the proxy custom backend that relays calls to one of the two standard client backends. The backend selection logic depends on the utilized path or on the file handle and is managed by additional data structures.

backend.c

#include <vfs/vfs.h>

#include <stdio.h>

#include <stdlib.h>

#include <platform/compiler.h>

#include <pthread.h>

#include <errno.h>

#include <string.h>

#include <getopt.h>

#include <assert.h>

/* Code for managing file handles. */

#define MAX_FDS 50

struct entry

{

Handle handle;

bool is_vfat;

};

struct fd_array

{

struct entry entries[MAX_FDS];

int pos;

pthread_rwlock_t lock;

};

struct fd_array fds = { .pos = 0, .lock = PTHREAD_RWLOCK_INITIALIZER };

int insert_entry(Handle fd, bool is_vfat)

{

pthread_rwlock_wrlock(&fds.lock);

if (fds.pos == MAX_FDS)

{

pthread_rwlock_unlock(&fds.lock);

return -1;

}

fds.entries[fds.pos].handle = fd;

fds.entries[fds.pos].is_vfat = is_vfat;

fds.pos++;

pthread_rwlock_unlock(&fds.lock);

return 0;

}

struct entry *find_entry(Handle fd)

{

pthread_rwlock_rdlock(&fds.lock);

for (int i = 0; i < fds.pos; i++)

{

if (fds.entries[i].handle == fd)

{

pthread_rwlock_unlock(&fds.lock);

return &fds.entries[i];

}

}

pthread_rwlock_unlock(&fds.lock);

return NULL;

}

/* Custom backend structure. */

struct context

{

struct vfs wrapper;

pthread_rwlock_t lock;

struct vfs *vfs_vfat;

struct vfs *vfs_ext4;

};

struct context ctx =

{

.wrapper =

{

.dtor = _vfs_backend_dtor,

.disconnect_all_clients = _disconnect_all_clients,

.getstdin = _getstdin,

.getstdout = _getstdout,

.getstderr = _getstderr,

.open = _open,

.read = _read,

.write = _write,

.close = _close,

}

};

/* Implementation of custom backend methods. */

static bool is_vfs_vfat_path(const char *path)

{

char vfat_path[5] = "/mnt1";

if (memcmp(vfat_path, path, sizeof(vfat_path)) != 0)

return false;

return true;

}

static void _vfs_backend_dtor(struct vfs *vfs)

{

ctx.vfs_vfat->dtor(ctx.vfs_vfat);

ctx.vfs_ext4->dtor(ctx.vfs_ext4);

}

static void _disconnect_all_clients(struct vfs *self, int *error)

{

(void)self;

(void)error;

ctx.vfs_vfat->disconnect_all_clients(ctx.vfs_vfat, error);

ctx.vfs_ext4->disconnect_all_clients(ctx.vfs_ext4, error);

}

static Handle _getstdin(struct vfs *self, int *error)

{

(void)self;

Handle handle = ctx.vfs_vfat->getstdin(ctx.vfs_vfat, error);

if (handle != INVALID_HANDLE)

{

if (insert_entry(handle, true))

{

*error = ENOMEM;

return INVALID_HANDLE;

}

}

return handle;

}

static Handle _getstdout(struct vfs *self, int *error)

{

(void)self;

Handle handle = ctx.vfs_vfat->getstdout(ctx.vfs_vfat, error);

if (handle != INVALID_HANDLE)

{

if (insert_entry(handle, true))

{

*error = ENOMEM;

return INVALID_HANDLE;

}

}

return handle;

}

static Handle _getstderr(struct vfs *self, int *error)

{

(void)self;

Handle handle = ctx.vfs_vfat->getstderr(ctx.vfs_vfat, error);

if (handle != INVALID_HANDLE)

{

if (insert_entry(handle, true))

{

*error = ENOMEM;

return INVALID_HANDLE;

}

}

return handle;

}

static Handle _open(struct vfs *self, const char *path, int oflag, mode_t mode, int *error)

{

(void)self;

Handle handle;

bool is_vfat = false;

if (is_vfs_vfat_path(path))

{

handle = ctx.vfs_vfat->open(ctx.vfs_vfat, path, oflag, mode, error);

is_vfat = true;

}

else

handle = ctx.vfs_ext4->open(ctx.vfs_ext4, path, oflag, mode, error);

if (handle == INVALID_HANDLE)

return INVALID_HANDLE;

if (insert_entry(handle, is_vfat))

{

if (is_vfat)

ctx.vfs_vfat->close(ctx.vfs_vfat, handle, error);

*error = ENOMEM;

return INVALID_HANDLE;

}

return handle;

}

static ssize_t _read(struct vfs *self, Handle fd, void *buf, size_t count, bool *nodata, int *error)

{

(void)self;

struct entry *found_entry = find_entry(fd);

if (found_entry != NULL && found_entry->is_vfat)

return ctx.vfs_vfat->read(ctx.vfs_vfat, fd, buf, count, nodata, error);

return ctx.vfs_ext4->read(ctx.vfs_ext4, fd, buf, count, nodata, error);

}

static ssize_t _write(struct vfs *self, Handle fd, const void *buf, size_t count, int *error)

{

(void)self;

struct entry *found_entry = find_entry(fd);

if (found_entry != NULL && found_entry->is_vfat)

return ctx.vfs_vfat->write(ctx.vfs_vfat, fd, buf, count, error);

return ctx.vfs_ext4->write(ctx.vfs_ext4, fd, buf, count, error);

}

static int _close(struct vfs *self, Handle fd, int *error)

{

(void)self;

struct entry *found_entry = find_entry(fd);

if (found_entry != NULL && found_entry->is_vfat)

return ctx.vfs_vfat->close(ctx.vfs_vfat, fd, error);

return ctx.vfs_ext4->close(ctx.vfs_ext4, fd, error);

}

/* Custom backend builder. ctx.vfs_vfat and ctx.vfs_ext4 are initialized

* as standard backends named "client". */

static struct vfs *_vfs_backend_create(Handle client_id, const char *config, int *error)

{

(void)config;

ctx.vfs_vfat = _vfs_init("client", client_id, "VFS1", error);

assert(ctx.vfs_vfat != NULL && "Can't initialize client backend!");

assert(ctx.vfs_vfat->dtor != NULL && "VFS FS backend has not set the destructor!");

ctx.vfs_ext4 = _vfs_init("client", client_id, "VFS2", error);

assert(ctx.vfs_ext4 != NULL && "Can't initialize client backend!");

assert(ctx.vfs_ext4->dtor != NULL && "VFS FS backend has not set the destructor!");

return &ctx.wrapper;

}

/* Registration of the custom backend under the name custom_client. */

static void _vfs_backend(create_vfs_backend_t *ctor, const char **name)

{

*ctor = &_vfs_backend_create;

*name = "custom_client";

}

REGISTER_VFS_BACKEND(_vfs_backend)

Linking of the Client program and the custom_client backend

Compile the written backend into a library:

CMakeLists.txt

add_library (backend_client STATIC "src/backend.c")

Link the prepared backend_client library to the Client program:

CMakeLists.txt (fragment)

add_dependencies (Client vfs_backend_client backend_client)

target_link_libraries (Client

pthread

${vfs_CLIENT_LIB}

"-Wl,--whole-archive" backend_client "-Wl,--no-whole-archive" backend_client

)

Writing the Env program

We use the Env program to pass arguments and environment variables to processes.

env.c

#include <env/env.h>

#include <stdlib.h>

int main(int argc, char** argv)

{

/* Mount fat32 to /mnt1 for the VfsFirst process and mount ext4 to /mnt2 for the VfsSecond process. */

const char* VfsFirstArgs[] = {

"-l", "ahci0 /mnt1 fat32 0"

};

ENV_REGISTER_ARGS("VfsFirst", VfsFirstArgs);

const char* VfsSecondArgs[] = {

"-l", "ahci1 /mnt2 ext4 0"

};

ENV_REGISTER_ARGS("VfsSecond", VfsSecondArgs);

/* Define the file backends. */

const char* vfs_first_args[] = { "_VFS_FILESYSTEM_BACKEND=server:VFS1" };

ENV_REGISTER_VARS("VfsFirst", vfs_first_args);

const char* vfs_second_args[] = { "_VFS_FILESYSTEM_BACKEND=server:VFS2" };

ENV_REGISTER_VARS("VfsSecond", vfs_second_args);

const char* client_fs_envs[] = { "_VFS_FILESYSTEM_BACKEND=custom_client:VFS1,VFS2" };

ENV_REGISTER_VARS("Client", client_fs_envs);

envServerRun();

return EXIT_SUCCESS;

}

Editing init.yaml

For the IPC channels that connect the Client process to the VfsFirst and VfsSecond processes, you must define the same names that you specified in the _VFS_FILESYSTEM_BACKEND environment variable: VFS1 and VFS2.

init.yaml

entities:

- name: vfs_backend.Env

- name: vfs_backend.Client

connections:

- target: vfs_backend.Env

id: {var: ENV_SERVICE_NAME, include: env/env.h}

- target: vfs_backend.VfsFirst

id: VFS1

- target: vfs_backend.VfsSecond

id: VFS2

- name: vfs_backend.VfsFirst

connections:

- target: vfs_backend.Env

id: {var: ENV_SERVICE_NAME, include: env/env.h}

- name: vfs_backend.VfsSecond

connections:

- target: vfs_backend.Env

id: {var: ENV_SERVICE_NAME, include: env/env.h}

Page top
[Topic vfs_backends]