embed_ext2 example

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:

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

Page top