Contents
VFS: overview
The VFS component contains the implementations of file systems and the network stack. POSIX calls for working with file systems and the network are sent to the VFS component, which then calls the block device driver or network driver, respectively.
Multiple copies of the VFS component can be added to a solution to separate the data streams of different entities. Each VFS copy is built separately and can contain the entire VFS functionality or a specific part of it, for example:
- One or more file systems
- Network stack
- Network stack and network driver
The VFS component can be used either directly (through static linking) or via IPC (as a separate entity). Use of VFS functionality via IPC enables the solution developer to do the following:
- Use a solution security policy to control method calls that work with the network and file systems.
- Connect multiple client entities to one VFS entity.
- Connect one client entity to two VFS entities to separately work with the network and file systems.
Building a VFS entity
KasperskyOS Community Edition does not provide a ready-to-use image for an entity containing the VFS component. The solution developer can independently build one or more entities that each include the specific VFS functionality necessary for the solution.
Please also refer to the multi_vfs_dhcpcd, multi_vfs_dns_client and multi_vfs_ntpd examples provided in KasperskyOS Community Edition.
Building a network VFS
To build a "network" VFS entity containing a network driver, the file containing the main()
function must be linked to the vfs_server
, vfs_net
and dnet_implementation
libraries:
CMakeLists.txt (fragment)
target_link_libraries (Net1Vfs ${vfs_SERVER_LIB}
${vfs_NET_LIB}
${dnet_IMPLEMENTATION_LIB})
set_target_properties (Net1Vfs PROPERTIES ${blkdev_ENTITY}_REPLACEMENT "")
Using a "network" VFS entity linked to a network driver
To use a network driver via IPC (as a separate entity), the dnet_client
library must be used instead of the dnet_implementation
library:
CMakeLists.txt (fragment)
target_link_libraries (Net2Vfs ${vfs_SERVER_LIB}
${vfs_NET_LIB}
${dnet_CLIENT_LIB})
set_target_properties (Net2Vfs PROPERTIES ${blkdev_ENTITY}_REPLACEMENT "")
Using a "network" VFS entity and a network driver as a separate entity
File operations are used by some functions, including printing to stdout
. For these functions to work correctly, the vfs_implementation
library must be added instead of vfs_net
during the build.
Building a file VFS
To build a "file" VFS entity, the file containing the main()
function must be linked to the vfs_server
and vfs_fs
libraries and to the libraries for implementing file systems:
CMakeLists.txt (fragment)
target_link_libraries (VfsFs
${vfs_SERVER_LIB}
${LWEXT4_LIB}
${vfs_FS_LIB})
set_target_properties (VfsFs PROPERTIES ${blkdev_ENTITY}_REPLACEMENT ${ramdisk_ENTITY})
In this example, the VFS entity is prepared to connect to the ramdisk driver entity.
A block device driver cannot be linked to VFS and is always used via IPC:
Using a "file" VFS entity and a driver as a separate entity
If necessary, you can build a VFS entity containing the network stack and the file systems. To do so, use the vfs_server
, vfs_implementation
, and dnet_implementation
libraries (or dnet_client
), and the file system implementation libraries.
Env entity
The Env
service entity allows running entities to pass arguments and environment variables. When started, each entity automatically sends a request to the Env
entity and receives the necessary data.
By including the Env
entity in the solution, you can mount file systems when VFS is started, connect one client entity to two VFS entities, and perform many other tasks.
To use the Env
entity in your solution, the following is required:
1. Develop the code of the Env
entity by using macros from env/env.h
.
2. Build an image of the entity by linking it to the env_server
library.
3. In the init description, indicate that the Env
entity must be started and connected to the selected entities (Env
acts a server in this case). The channel name is defined by the ENV_SERVICE_NAME
macro declared in the env/env.h
file.
4. Include the Env
entity image in the solution image.
Env entity code
The code of the Env
entity utilizes the following macros and functions declared in the env/env.h
file:
ENV_REGISTER_ARGS(name,argarr)
– arguments from theargarr
array will be passed to the entity namedname
.ENV_REGISTER_VARS(name,envarr)
– environment variables from theenvarr
array will be passed to the "name" entity.ENV_REGISTER_PROGRAM_ENVIRONMENT(name,argarr,envarr)
– arguments and environment variables will be passed to the entity namedname
.envServerRun()
– initialize the server part of the entity so that it can respond to requests.
Example:
env.c
int main(int argc, char** argv)
{
const char* NetVfsArgs[] = {
"-l", "devfs /dev devfs 0",
"-l", "romfs /etc romfs 0"
};
ENV_REGISTER_ARGS("VFS", NetVfsArgs);
envServerRun();
return EXIT_SUCCESS;
}
Init.yaml example for use of the Env entity
In the next example, the Client
entity will be connected to the Env
entity whose image is located in the env
folder:
init.yaml
entities:
- name: env.Env
- name: Client
connections:
- target: env.Env
id: {var: ENV_SERVICE_NAME, include: env/env.h}
Connecting a client entity to one or two VFS entities
Calls of network- and file POSIX functions can be forwarded to two separate VFS components by connecting a client entity to two different VFS entities. If such data stream separation is not required (for example, if the client only works with the network), connecting the client entity to a single VFS entity is enough.
Connecting to one VFS entity
The name of the IPC channel between the client entity and the VFS entity must be defined by the _VFS_CONNECTION_ID
macro declared in the vfs/defs.h
file. In this case, "network" calls and "file" calls will be sent to this VFS entity.
Example:
init.yaml
- name: ClientEntity
connections:
- target: VfsEntity
id: {var: _VFS_CONNECTION_ID, include: vfs/defs.h}
- name: VfsEntity
Connecting to two VFS entities
Let's examine a client entity that is connected to two different VFS entities. We will name them "network" VFS and "file" VFS.
The _VFS_NETWORK_BACKEND
environment variable is used so that "network" calls from the client entity are sent only to the "network" VFS:
- For the "network" VFS entity:
_VFS_NETWORK_BACKEND=server:<name of the IPC channel to the "network" VFS>
- For the client entity:
_VFS_NETWORK_BACKEND=client: <name of the IPC channel to the "network" VFS>
The analogous _VFS_FILESYSTEM_BACKEND
environment variable is used to send "file" calls:
- For the "file" VFS entity:
_VFS_FILESYSTEM_BACKEND=server:<name of the IPC channel to the "file" VFS>
- For the client entity:
_VFS_FILESYSTEM_BACKEND=client: <name of the IPC channel to the "file" VFS>
As a result, the functions for working with the network and files will be sent to two different VFS entities.
In the next example, the Client
entity is connected to two VFS entities – the "network" VfsFirst
entity and the "file" VfsSecond
entity:
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}
Env entity code:
env.c
int main(void)
{
const char* vfs_first_args[] = { "_VFS_NETWORK_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_envs[] = { "_VFS_NETWORK_BACKEND=client:VFS1", "_VFS_FILESYSTEM_BACKEND=client:VFS2" };
ENV_REGISTER_VARS("Client", client_envs);
envServerRun();
return EXIT_SUCCESS;
}
Please also refer to the multi_vfs_dhcpcd, multi_vfs_dns_client and multi_vfs_ntpd examples provided in KasperskyOS Community Edition.
Mounting file systems when VFS starts
By default, the VFS component provides access to the following:
- RAMFS file system. RAMFS is mounted to the root directory by default.
- ROMFS object storage. The storage contains non-executable files (including configuration files) that are added to the solution image during the build. The ROMFS file system is not mounted by default. However, the storage can be accessed indirectly through the
-f
argument, for example.
If you need to mount other file systems, this can be done either by using the mount()
call after the VFS starts or immediately when the VFS starts by passing the following arguments and environment variables to it:
-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 theUMNAP_ROMFS
variable is defined, the file system mounted using theROOTFS
variable will be searched for the file.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 theUNMAP_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.
Example:
env.c
int main(int argc, char** argv)
{
/* The devfs and romfs file systems will be mounted for the Vfs1 entity. */
const char* Vfs1Args[] = {
"-l", "devfs /dev devfs 0",
"-l", "romfs /etc romfs 0"
};
ENV_REGISTER_ARGS("Vfs1", Vfs1Args);
/* The file systems defined through the /etc/dhcpcd.conf file located in the ROMFS storage will be mounted for the Vfs2 entity. */
const char* Vfs2Args[] = { "-f", "/etc/dhcpcd.conf" };
ENV_REGISTER_ARGS("Vfs2", Vfs2Args);
/* The ext2 file system containing the /etc/fstab file used for mounting additional file systems will be mounted to the root directory for the Vfs3 entity. The ROMFS storage will be deleted. */
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;
}
Please also refer to the net_with_separate_vfs, net2_with_separate_vfs, multi_vfs_dhcpcd, multi_vfs_dns_client and multi_vfs_ntpd examples provided in KasperskyOS Community Edition.