This example shows how to embed a new file system into the virtual file system (VFS) that is provided in KasperskyOS Community Edition.
In this example, the Client
entity tests the operation of file systems (ext2, ext3, ext4) on block devices. To do so, the Client
calls the driver of the Ext2
file system via IPC, and Ext2
in turn calls the block device via IPC.
The ext2 and ext3 file systems work with the default settings. The ext4 file system works if you disable everything except the log (mkfs.ext4 -O ^extent,^huge_file,^flex_bg,^uninit_bg,^dir_nlink,^extra_isize /dev/foo
).
Supplied resources
The example includes the following images of hard drives with file systems:
hdd_arm.img
contains one partition each for the ext2, ext3, and ext4 file systems.hdd_ext2.img
contains a partition with the ext2 file system.hdd_ext3.img
contains a partition with the ext3 file system.hdd_ext4.img
contains a partition with the ext4 file system.Only one SD card is supported for the Arm architecture. For this reason, the hdd_arm.img
image is used.
This example does not contain an implementation of drivers of block devices used by the Client
(the Ata
and Sdcard
entities). These drivers are provided in KasperskyOS Community Edition and are added in the build file ./CMakeLists.txt
.
Porting the implementation of the ext2, ext3, and ext4 file systems
Ported files with source code from the lwext4
project are located in the following folder:
./ext2/lwext4/src
The implementation of methods for working with index nodes is located in the following file:
ext2/lwext4/src/kos/ext.c
static struct inode_operations _inode_ops =
{
.create = _folder_create_file,
.mkdir = _folder_create_dir,
.unlink = _folder_unlink,
.link = _folder_link,
.symlink = _folder_symlink,
.ftruncate = _ftruncate,
.rename = _folder_rename,
.readdir = _folder_readdir,
.read = _read_file,
.read_symname = _read_symname,
.write = _write_file,
.compare = NULL,
};
EDL descriptions of entities
Client.edl
entity client.Client
Ext2.edl
entity ext2.Ext2
components {
vfs: kl.Vfs
}
Implementation of the `Client` entity
main.c
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mount.h>
#include <sys/types.h>
enum { MAX_FS_PATH_SIZE = 1024 };
#define TEST_MESSAGE "test_message"
#define TEST_MESSAGE_LEN 12
struct mount_point {
int device_num;
int partition_num;
char *fs_name;
};
/* Mount points for the ext2, ext3, and ext4 file systems. */
#ifdef __arm__
static struct mount_point mount_points[] =
{
{ 0, 0, "ext2" },
{ 0, 1, "ext3" },
{ 0, 2, "ext4" }
};
#else
static struct mount_point mount_points[] =
{
{ 0, 0, "ext2" },
{ 1, 0, "ext3" },
{ 2, 0, "ext4" }
};
#endif
/* Mounts the corresponding file system for each test. */
static int prepare_mount_point(int case_num, const char *path)
{
char fs_path[MAX_FS_PATH_SIZE];
if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO)) {
printf("Failed to create \"%s\" dir. error %d: \"%s\"\n",
path,
errno,
strerror(errno));
return -1;
}
sprintf(fs_path, "%s%d,%d",
BLKDEV,
mount_points[case_num].device_num,
mount_points[case_num].partition_num);
if (mount(fs_path, path, mount_points[case_num].fs_name, 0, "")) {
printf("Failed to mount %s, %s, %s. error %d: \"%s\"\n",
fs_path,
path,
mount_points[case_num].fs_name,
errno,
strerror(errno));
return -1;
}
return 0;
}
static int perform_test(const char *fs_name, const char *mount_point)
{
char file_name[MAX_FS_PATH_SIZE];
char buffer[TEST_MESSAGE_LEN + 1];
int ret;
int fd;
strcpy(file_name, mount_point);
strcat(file_name, "new_file");
memset(buffer, 0, TEST_MESSAGE_LEN);
fd = open(file_name, O_RDWR | O_CREAT);
if (fd == -1) {
printf("Failed to create new file new_file (error %d: \"%s\")\n",
errno,
strerror(errno));
return -1;
}
ret = write(fd, TEST_MESSAGE, TEST_MESSAGE_LEN);
if (ret != TEST_MESSAGE_LEN) {
printf("Failed to write message to new_file. write() returned %d\n",
ret);
return -1;
}
if (close(fd)) {
printf("Failed to close new_file (error %d: \"%s\")\n",
errno,
strerror(errno));
return -1;
}
fd = open(file_name, O_RDONLY);
if (fd == -1) {
printf("Failed to open existing file new_file (error %d: \"%s\")\n",
errno,
strerror(errno));
return -1;
}
ret = read(fd, buffer, TEST_MESSAGE_LEN);
if (ret != TEST_MESSAGE_LEN) {
printf("Failed to read message from new_file. read() returned %d\n",
ret);
return -1;
}
buffer[TEST_MESSAGE_LEN] = '\0';
printf("%s: %s\n", fs_name, buffer);
if (close(fd)) {
printf("Failed to finally close new_file (error %d: \"%s\")\n",
errno,
strerror(errno));
return -1;
}
if (unlink(file_name)) {
printf("Failed to finally unlink new_file (error %d: \"%s\")\n",
errno,
strerror(errno));
return -1;
}
return 0;
}
int main(void)
{
size_t i;
if (prepare_mount_point(0, "/c")) {
return EXIT_FAILURE;
}
perform_test("ext2", "/c");
if (prepare_mount_point(1, "/d")) {
return EXIT_FAILURE;
}
perform_test("ext3", "/d");
if (prepare_mount_point(2, "/e")) {
return EXIT_FAILURE;
}
perform_test("ext4", "/e");
if (umount("/e") != 0) {
printf("Failed to umount /e. error %d: \"%s\"\n",
errno,
strerror(errno));
return EXIT_FAILURE;
}
if (umount("/d") != 0) {
printf("Failed to umount /d. error %d: \"%s\"\n",
errno,
strerror(errno));
return EXIT_FAILURE;
}
if (umount("/c") != 0) {
printf("Failed to umount /c. error %d: \"%s\"\n",
errno,
strerror(errno));
return EXIT_FAILURE;
}
printf("All devices successfully unmounted\n");
return EXIT_SUCCESS;
}
Init description
The Client
entity works with the block device through the driver of the ext2
file system.
init.yaml.in
entities:
- name: client.Client
path: client
connections:
- target: ext2.Ext2
id: {var: _VFS_CONNECTION_ID, include: vfs/defs.h}
@INIT_Client_ENTITY_CONNECTIONS+@
- name: ext2.Ext2
path: ext2
@INIT_ext2_ENTITY_CONNECTIONS@
@INIT_EXTERNAL_ENTITIES@
Security configuration
The security configuration in this example allows all entities to run, and allows any entity to query the Core
entity, the ext2
entity, and the block device driver.
common.psl
/* Security configuration of the "embed_ext2" example. */
use nk.base._
/* Declaration of entities. */
use EDL kl.core.Core
use EDL Einit
use EDL client.Client
use EDL ext2.Ext2
/* Startup of entities is allowed. */
execute {
grant ()
}
/* Request messages are allowed */
request {
grant ()
}
/* Response messages are allowed */
response {
grant ()
}
/* Calls to the security interface are ignored. */
security {
grant ()
}
security_arm.psl
/* Common security policy */
use common._
/* Only for Arm */
use EDL kl.drivers.SDCard
use EDL kl.drivers.BSP
security_x86.psl
/* Common security policy */
use common._
/* Only for x86 */
use EDL kl.drivers.ATA
use EDL kl.drivers.PCIE
Build configuration
EDL descriptions of the Client
and Ext2
entities are generated directly in CMake scripts using the build_entity
function provided by NK.
Client entity build instructions
./client/CMakeLists.txt
project (client)
# Tools for using the NK parser.
include (platform/nk)
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "arm")
set (BLKDEV "mmc")
else ()
set (BLKDEV "Ahci0Port")
endif ()
nk_build_edl_files (client_edl_files NK_MODULE "client" EDL "${CMAKE_SOURCE_DIR}/resources/edl/Client.edl")
add_executable (client "src/main.c")
add_dependencies (client client_edl_files)
set_target_compiler_flags_default (client "STANDARD_GNU_11:YES" "STRICT_WARNINGS:YES")
target_link_libraries (client ${vfs_CLIENT_LIB})
set_target_properties (client PROPERTIES COMPILE_DEFINITIONS BLKDEV="${BLKDEV}")
#set_target_properties (client PROPERTIES LINK_FLAGS "-Ttext 0x00800000")
# We do not need default VFS entity here, which comes from ${vfs_CLIENT_LIB}
set_target_properties (client PROPERTIES ${vfs_ENTITY}_REPLACEMENT "")
Ext2 entity build instructions
./ext2/CMakeLists.txt
project (ext2)
# Implementation of ext2/3/4 file systems
set (HAVE_READDIR YES)
add_subdirectory (lwext4)
# Tools for using the NK parser.
include (platform/nk)
nk_build_edl_files (ext2_edl_files NK_MODULE "ext2" EDL "${CMAKE_SOURCE_DIR}/resources/edl/Ext2.edl")
# Target - ext2
add_executable (ext2 "main.c")
add_dependencies (ext2 ext2_edl_files)
# Sets the compiler flags.
set_target_compiler_flags_default (ext2 "STANDARD_GNU_C99:YES")
# vfs_SERVER_LIB is the implementation of the server portion of the file system entity.
# vfs_IMPLEMENTATION_LIB is the implementation of the virtual file system and
# ROMFS and RAMFS file systems.
target_link_libraries (ext2 ${vfs_SERVER_LIB} ${vfs_FS_LIB} "-Wl,--whole-archive" lwext4 "-Wl,--no-whole-archive")
#set_target_properties (ext2 PROPERTIES LINK_FLAGS "-Ttext 0x02800000")
Instructions for building the Einit entity and the system image
./einit/CMakeLists.txt
project (einit)
# Tools for working with KasperskyOS images.
include (platform/image)
# Set compile flags.
project_header_default ("STANDARD_GNU_11:YES" "STRICT_WARNINGS:NO")
if ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm|aarch64")
set(SECURITY_PSL_FILE src/security_arm.psl)
# Connects a package that imports components for working with the SD card.
find_package (sdcard REQUIRED)
include_directories (${sdcard_INCLUDE})
# We need SDCARD driver to be connected with our implementation of VFS
set_target_properties (ext2 PROPERTIES ${blkdev_ENTITY}_REPLACEMENT ${sdcard_ENTITY})
# Adds a target and copies the disk image to the example build folder.
add_custom_target (hdd.img
COMMAND ${CMAKE_COMMAND} -E make_directory
${CMAKE_BINARY_DIR}/hdd_part1
COMMAND ${CMAKE_COMMAND} -E make_directory
${CMAKE_BINARY_DIR}/hdd_part2
COMMAND ${CMAKE_COMMAND} -E make_directory
${CMAKE_BINARY_DIR}/hdd_part3
COMMAND ${CMAKE_COMMAND} -E copy_directory
${KL_SDK_ROOT_PATH}/common/hdd ${CMAKE_BINARY_DIR}/hdd_part1
COMMAND ${CMAKE_COMMAND} -E copy_directory
${KL_SDK_ROOT_PATH}/common/hdd ${CMAKE_BINARY_DIR}/hdd_part2
COMMAND ${CMAKE_COMMAND} -E copy_directory
${KL_SDK_ROOT_PATH}/common/hdd ${CMAKE_BINARY_DIR}/hdd_part3
COMMAND ${KL_SDK_ROOT_PATH}/common/prepare_hdd_img.sh -s 128 -%1 33 -%2 33 -%3 33 -p1 ${CMAKE_BINARY_DIR}/hdd_part1 -p2 ${CMAKE_BINARY_DIR}/hdd_part2 -p3 ${CMAKE_BINARY_DIR}/hdd_part3 -img hdd.img)
# Only one SD card is supported for the Arm architecture.
set (QEMU_FLAGS "-nographic -monitor none -sd hdd.img")
set (QEMU_DEPENDENCIES hdd.img)
else ()
set(SECURITY_PSL_FILE src/security_x86.psl)
# Connects a package that imports components for working with the ata device.
find_package (ata REQUIRED)
include_directories (${ata_INCLUDE})
# We need ATA driver to be connected with our implementation of VFS
set_target_properties (ext2 PROPERTIES ${blkdev_ENTITY}_REPLACEMENT ${ata_ENTITY})
# Adds targets and copies disk images to the example build folder.
add_custom_target (hdd1.img
COMMAND ${CMAKE_COMMAND} -E make_directory
${CMAKE_BINARY_DIR}/hdd1
COMMAND ${CMAKE_COMMAND} -E copy_directory
${KL_SDK_ROOT_PATH}/common/hdd ${CMAKE_BINARY_DIR}/hdd1
COMMAND ${KL_SDK_ROOT_PATH}/common/prepare_hdd_img.sh -d ${CMAKE_BINARY_DIR}/hdd1 -img hdd1.img -f ext2)
add_custom_target (hdd2.img
COMMAND ${CMAKE_COMMAND} -E make_directory
${CMAKE_BINARY_DIR}/hdd2
COMMAND ${CMAKE_COMMAND} -E copy_directory
${KL_SDK_ROOT_PATH}/common/hdd ${CMAKE_BINARY_DIR}/hdd2
COMMAND ${KL_SDK_ROOT_PATH}/common/prepare_hdd_img.sh -d ${CMAKE_BINARY_DIR}/hdd2 -img hdd2.img -f ext2)
add_custom_target (hdd3.img
COMMAND ${CMAKE_COMMAND} -E make_directory
${CMAKE_BINARY_DIR}/hdd3
COMMAND ${CMAKE_COMMAND} -E copy_directory
${KL_SDK_ROOT_PATH}/common/hdd ${CMAKE_BINARY_DIR}/hdd3
COMMAND ${KL_SDK_ROOT_PATH}/common/prepare_hdd_img.sh -d ${CMAKE_BINARY_DIR}/hdd3 -img hdd3.img -f ext2)
set (QEMU_FLAGS "-nographic -monitor none \
-device ahci,id=ahci \
-drive id=disk1,file=hdd1.img,if=none -device ide-drive,drive=disk1,bus=ahci.0 \
-drive id=disk2,file=hdd2.img,if=none -device ide-drive,drive=disk2,bus=ahci.1 \
-drive id=disk3,file=hdd3.img,if=none -device ide-drive,drive=disk3,bus=ahci.2")
set (QEMU_DEPENDENCIES hdd1.img hdd2.img hdd3.img)
endif ()
# KasperskyOS image for target hardware platform.
build_kos_hw_image (kos-image
EINIT_ENTITY EinitHw
CONNECTIONS_CFG "src/init.yaml.in"
SECURITY_PSL "${SECURITY_PSL_FILE}"
IMAGE_FILES client ext2)
# KasperskyOS image for QEMU with simulation targets (sim, gdbsim, gdb).
build_kos_qemu_image (kos-qemu-image
EINIT_ENTITY EinitQemu
CONNECTIONS_CFG "src/init.yaml.in"
SECURITY_PSL "${SECURITY_PSL_FILE}"
IMAGE_FILES client ext2
QEMU_FLAGS "${QEMU_FLAGS}"
QEMU_DEPENDENCIES "${QEMU_DEPENDENCIES}")
#set_target_properties (EinitQemu PROPERTIES LINK_FLAGS "-Ttext 0x00200000")
Solution build instructions
./CMakeLists.txt
cmake_minimum_required (VERSION 3.12)
# Initialize the CMake library
include (platform)
initialize_platform ()
# Add Doxygen documentation
include (platform/doxygen)
add_project_documentation_main_target ()
# Add functions for installing artefacts.
include (platform/install)
# Add package importing components for working with
# the virtual file system.
find_package (vfs REQUIRED COMPONENTS FS_LIB CLIENT_LIB SERVER_LIB)
include_directories (${vfs_INCLUDE})
add_subdirectory (client)
add_subdirectory (ext2)
add_subdirectory (einit)
Build using CMake
To build and run the example, run the following script:
/opt/KasperskyOS-Community-Edition-<version>/examples/embed_ext2/cross-build.sh