Пример показывает, как встроить новую файловую систему в виртуальную файловую систему (VFS), поставляемую в составе KasperskyOS Community Edition.
В этом примере сущность Client
тестирует работу файловых систем (ext2, ext3, ext4) на блочных устройствах. Для этого Client
обращается по IPC к драйверу файловой системы Ext2
, а Ext2
в свою очередь обращается по IPC к блочному устройству.
Файловые системы ext2 и ext3 работают с настройками по умолчанию. Файловая система ext4 работает, если отключить все, кроме журнала (mkfs.ext4 -O ^extent,^huge_file,^flex_bg,^uninit_bg,^dir_nlink,^extra_isize /dev/foo
).
Поставляемые ресурсы
В пример входят следующие образы жестких дисков с файловыми системами:
hdd_arm.img
содержит по одному разделу для файловых систем ext2, ext3, ext4.hdd_ext2.img
содержит раздел с файловой системой ext2.hdd_ext3.img
содержит раздел с файловой системой ext3.hdd_ext4.img
содержит раздел с файловой системой ext4.Для архитектуры Arm поддерживается работа только с одной SD-картой, поэтому используется образ hdd_arm.img
.
Этот пример не содержит реализации драйверов блочных устройств, с которыми работает Client
(сущности Ata
, Sdcard
). Эти драйверы поставляются в составе KasperskyOS Community Edition и добавляются в файле сборки ./CMakeLists.txt
.
Портирование реализации файловых систем ext2, ext3 и ext4
Портированные файлы с исходным кодом из проекта lwext4
находятся в следующей директории:
./ext2/lwext4/src
Имплементация методов для работы с индексными узлами находится в следующем файле:
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-описания сущностей
Client.edl
entity client.Client
Ext2.edl
entity ext2.Ext2
components {
vfs: kl.Vfs
}
Реализация сущности Client
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;
};
/* Точки монтирования для файловых систем ext2, ext3, ext4. */
#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
/* Функция, монтирующая соответствующую файловую систему для каждого теста. */
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-описание
Сущность Client
работает с блочным устройством через драйвер файловой системы ext2
.
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@
Конфигурация безопасности
Конфигурация безопасности в этом примере разрешает запуск всех сущностей и позволяет любой сущности обращаться к сущностям Core
, ext2
и к драйверу блочного устройства.
common.psl
/* Конфигурация безопасности для примера "embed_ext2". */
use nk.base._
/* Объявление сущностей. */
use EDL kl.core.Core
use EDL Einit
use EDL client.Client
use EDL ext2.Ext2
/* Запуск сущностей разрешен. */
execute {
grant ()
}
/* Сообщения типа request разрешены */
request {
grant ()
}
/* Сообщения типа response разрешены */
response {
grant ()
}
/* Вызовы к security interface игнорируются. */
security {
grant ()
}
security_arm.psl
/* Общая политика безопасности */
use common._
/* Только для arm */
use EDL kl.drivers.SDCard
use EDL kl.drivers.BSP
security_x86.psl
/* Общая политика безопасности */
use common._
/* Только для x86 */
use EDL kl.drivers.ATA
use EDL kl.drivers.PCIE
Конфигурация сборки
Генерация EDL-описаний сущностей Client
и Ext2
осуществляется непосредственно в скриптах CMake с помощью функции build_entity
, которую предоставляет NK.
Инструкции сборки сущности Client
./client/CMakeLists.txt
project (client)
# Инструментарий для работы с парсером NK.
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
./ext2/CMakeLists.txt
project (ext2)
# Реализация файловых систем ext2/3/4
set (HAVE_READDIR YES)
add_subdirectory (lwext4)
# Инструментарий для работы с парсером NK.
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)
# Установка флагов компилятора.
set_target_compiler_flags_default (ext2 "STANDARD_GNU_C99:YES")
# vfs_SERVER_LIB - реализация серверной части сущности файловой системы.
# vfs_IMPLEMENTATION_LIB - реализация виртуальной файловой системы и
# файловых систем ROMFS, RAMFS.
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")
Инструкции сборки сущности Einit и образа системы
./einit/CMakeLists.txt
project (einit)
# Инструментарий для работы с образами KasperskyOS.
include (platform/image)
# Установка флагов компиляции.
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)
# Подключение пакета, импортирующего компоненты для работы с SD-картой.
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})
# Добавляем цель с копированием образа диска в директорию сборки примера.
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)
# Для архитектуры Arm поддерживается работа только с одной SD-картой.
set (QEMU_FLAGS "-nographic -monitor none -sd hdd.img")
set (QEMU_DEPENDENCIES hdd.img)
else ()
set(SECURITY_PSL_FILE src/security_x86.psl)
# Подключение пакета, импортирующего компоненты для работы с ata-устройством.
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})
# Добавляем цели с копированием образов дисков в директорию сборки примера.
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 для аппаратной платформы.
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 для QEMU с целями эмуляции (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")
Инструкции сборки решения
./CMakeLists.txt
cmake_minimum_required (VERSION 3.12)
# Инициализация библиотеки CMake
include (platform)
initialize_platform ()
# Добавление документации в формате Doxygen
include (platform/doxygen)
add_project_documentation_main_target ()
# Подключение функций установки артефактов.
include (platform/install)
# Подключение пакета, импортирующего компоненты для работы с
# виртуальной файловой системой.
find_package (vfs REQUIRED COMPONENTS FS_LIB CLIENT_LIB SERVER_LIB)
include_directories (${vfs_INCLUDE})
add_subdirectory (client)
add_subdirectory (ext2)
add_subdirectory (einit)
Сборка с помощью CMake
Для сборки и запуска примера необходимо выполнить следующий скрипт:
/opt/KasperskyOS-Community-Edition-<version>/examples/embed_ext2/cross-build.sh