В этом примере показано, как создать и использовать собственный VFS-бэкенд.
Процесс Client
использует файловые системы fat32 и ext4. Процесс VfsFirst
обеспечивает работу с файловой системой fat32, а процесс VfsSecond
дает возможность работать с файловой системой ext4. Через переменные окружения программ, исполняющихся в контекстах процессов Client
, VfsFirst
и VfsSecond
, заданы VFS-бэкенды, которые обеспечивают обработку IPC-запросов процесса Client
процессом VfsFirst
или VfsSecond
в зависимости от того, какую файловую систему использует процесс Client
. В результате этого IPC-запросы процесса Client
, связанные с использованием файловой системы fat32, обрабатываются процессом VfsFirst
, а IPC-запросы процесса Client
, связанные с использованием файловой системы ext4, обрабатываются процессом VfsSecond
(см. рис. ниже).
На стороне процесса VfsFirst
файловая система fat32 монтируется в директорию /mnt1
. На стороне процесса VfsSecond
файловая система ext4 монтируется в директорию /mnt2
. Пользовательский VFS-бэкенд custom_client
, используемый на стороне процесса Client
, позволяет отправлять IPC-запросы по IPC-каналу VFS1
или VFS2
в зависимости от того, начинается ли путь к файлу с /mnt1
или нет. При этом пользовательский VFS-бэкенд использует в качестве посредника стандартный VFS-бэкенд client
.
Схема взаимодействия процессов
Исходный код VFS-бэкенда
Этот файл реализации содержит исходный код VFS-бэкенда custom_client
, использующего стандартные VFS-бэкенды client
:
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>
/* Код управления файловыми дескрипторами */
#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;
}
/* Структура пользовательского VFS-бэкенда */
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,
}
};
/* Реализация методов пользовательского VFS-бэкенда */
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);
}
/* Конструктор пользовательского VFS-бэкенда. ctx.vfs_vfat и ctx.vfs_ext4 инициализируются
* как стандартные бэкенды с именем "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;
}
/* Регистрация пользовательского VFS-бэкенда под именем 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)
Компоновка программы Client
Создание статической библиотеки VFS-бэкенда:
CMakeLists.txt
...
add_library (backend_client STATIC "src/backend.c")
...
Компоновка программы Client
со статической библиотеки VFS-бэкенда:
CMakeLists.txt
...
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
)
...
Установка параметров запуска и переменных окружения программ
Init-описание примера:
init.yaml
entities:
- name: vfs_backend.Client
connections:
- target: vfs_backend.VfsFirst
id: VFS1
- target: vfs_backend.VfsSecond
id: VFS2
env:
_VFS_FILESYSTEM_BACKEND: custom_client:VFS1,VFS2
- name: vfs_backend.VfsFirst
args:
- -l
- ahci0 /mnt1 fat32 0
env:
_VFS_FILESYSTEM_BACKEND: server:VFS1
- name: vfs_backend.VfsSecond
- -l
- ahci1 /mnt2 ext4 0
env:
_VFS_FILESYSTEM_BACKEND: server:VFS2
В начало