native FUSE FS access: Support any libC and fix random crashes
Split the fuse FS driver into 2 parts: A top built in the embedded side, with the embedded libC, and a bottom built in the runner side with the host libC. The error returns are converted to match the host libC. Also, before the host FUSE thread, which is asynchronous to Zephyr was calling directly into the Zephyr filesystem code, which resulted quite often if catastrophic failures or corruption of the Zephyr state. This is now fixed by having the FUSE thread queue requests to a Zephyr thread, which will be handled in the embedded side in a coherent way. This adds a slightly noticeable overhead, but the performance is still acceptable. Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
This commit is contained in:
parent
e44a9523d4
commit
ac24d2277e
@ -36,9 +36,12 @@ if(CONFIG_FUSE_FS_ACCESS)
|
||||
zephyr_include_directories(${FUSE_INCLUDE_DIRS})
|
||||
if (CONFIG_NATIVE_LIBRARY)
|
||||
target_link_options(native_simulator INTERFACE "-l${FUSE_LIBRARIES}")
|
||||
target_sources(native_simulator INTERFACE fuse_fs_access_bottom.c)
|
||||
target_compile_options(native_simulator INTERFACE "-D_FILE_OFFSET_BITS=64")
|
||||
else()
|
||||
zephyr_link_libraries(${FUSE_LIBRARIES})
|
||||
zephyr_library_sources(fuse_fs_access_bottom.c)
|
||||
zephyr_library_compile_definitions(_FILE_OFFSET_BITS=64)
|
||||
endif()
|
||||
zephyr_library_compile_definitions(_FILE_OFFSET_BITS=64)
|
||||
zephyr_library_sources(fuse_fs_access.c)
|
||||
endif()
|
||||
|
||||
@ -95,6 +95,7 @@ config FILE_SYSTEM_MKFS
|
||||
config FUSE_FS_ACCESS
|
||||
bool "FUSE based access to file system partitions"
|
||||
depends on ARCH_POSIX
|
||||
select NATIVE_USE_NSI_ERRNO
|
||||
help
|
||||
Expose file system partitions to the host system through FUSE.
|
||||
|
||||
|
||||
@ -1,46 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
|
||||
* Copyright (c) 2025 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define FUSE_USE_VERSION 26
|
||||
|
||||
#undef _XOPEN_SOURCE
|
||||
#define _XOPEN_SOURCE 700
|
||||
|
||||
#include <fuse.h>
|
||||
#include <libgen.h>
|
||||
#include <linux/limits.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/fs/fs.h>
|
||||
#include <nsi_errno.h>
|
||||
|
||||
#include "cmdline.h"
|
||||
#include "soc.h"
|
||||
|
||||
#define S_IRWX_DIR (0775)
|
||||
#define S_IRW_FILE (0664)
|
||||
#include "fuse_fs_access_bottom.h"
|
||||
|
||||
#define NUMBER_OF_OPEN_FILES 128
|
||||
#define INVALID_FILE_HANDLE (NUMBER_OF_OPEN_FILES + 1)
|
||||
|
||||
#define DIR_END '\0'
|
||||
|
||||
static struct fs_file_t files[NUMBER_OF_OPEN_FILES];
|
||||
static uint8_t file_handles[NUMBER_OF_OPEN_FILES];
|
||||
|
||||
static pthread_t fuse_thread;
|
||||
|
||||
static const char default_fuse_mountpoint[] = "flash";
|
||||
|
||||
static const char *fuse_mountpoint;
|
||||
@ -66,427 +44,221 @@ static void release_file_handle(size_t handle)
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_mount_point(const char *path)
|
||||
{
|
||||
char dir_path[PATH_MAX];
|
||||
size_t len;
|
||||
|
||||
len = strlen(path);
|
||||
if (len >= sizeof(dir_path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(dir_path, path, len);
|
||||
dir_path[len] = '\0';
|
||||
return strcmp(dirname(dir_path), "/") == 0;
|
||||
}
|
||||
|
||||
static int fuse_fs_access_getattr(const char *path, struct stat *stat)
|
||||
static int ffa_stat_top(const char *path, struct ffa_dirent *entry_bottom)
|
||||
{
|
||||
struct fs_dirent entry;
|
||||
int err;
|
||||
|
||||
stat->st_dev = 0;
|
||||
stat->st_ino = 0;
|
||||
stat->st_nlink = 0;
|
||||
stat->st_uid = getuid();
|
||||
stat->st_gid = getgid();
|
||||
stat->st_rdev = 0;
|
||||
stat->st_blksize = 0;
|
||||
stat->st_blocks = 0;
|
||||
stat->st_atime = 0;
|
||||
stat->st_mtime = 0;
|
||||
stat->st_ctime = 0;
|
||||
|
||||
if ((strcmp(path, "/") == 0) || is_mount_point(path)) {
|
||||
if (strstr(path, "/.") != NULL) {
|
||||
return -ENOENT;
|
||||
}
|
||||
stat->st_mode = S_IFDIR | S_IRWX_DIR;
|
||||
stat->st_size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = fs_stat(path, &entry);
|
||||
|
||||
if (err != 0) {
|
||||
return err;
|
||||
return nsi_errno_to_mid(-err);
|
||||
}
|
||||
|
||||
entry_bottom->size = entry.size;
|
||||
|
||||
if (entry.type == FS_DIR_ENTRY_DIR) {
|
||||
stat->st_mode = S_IFDIR | S_IRWX_DIR;
|
||||
stat->st_size = 0;
|
||||
entry_bottom->is_directory = true;
|
||||
} else {
|
||||
stat->st_mode = S_IFREG | S_IRW_FILE;
|
||||
stat->st_size = entry.size;
|
||||
entry_bottom->is_directory = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fuse_fs_access_readmount(void *buf, fuse_fill_dir_t filler)
|
||||
static int ffa_readmount_top(int *mnt_nbr, const char **mnt_name)
|
||||
{
|
||||
int mnt_nbr = 0;
|
||||
const char *mnt_name;
|
||||
struct stat stat;
|
||||
int err;
|
||||
int err = fs_readmount(mnt_nbr, mnt_name);
|
||||
|
||||
stat.st_dev = 0;
|
||||
stat.st_ino = 0;
|
||||
stat.st_nlink = 0;
|
||||
stat.st_uid = getuid();
|
||||
stat.st_gid = getgid();
|
||||
stat.st_rdev = 0;
|
||||
stat.st_atime = 0;
|
||||
stat.st_mtime = 0;
|
||||
stat.st_ctime = 0;
|
||||
stat.st_mode = S_IFDIR | S_IRWX_DIR;
|
||||
stat.st_size = 0;
|
||||
stat.st_blksize = 0;
|
||||
stat.st_blocks = 0;
|
||||
|
||||
filler(buf, ".", &stat, 0);
|
||||
filler(buf, "..", NULL, 0);
|
||||
|
||||
do {
|
||||
err = fs_readmount(&mnt_nbr, &mnt_name);
|
||||
if (err < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
filler(buf, &mnt_name[1], &stat, 0);
|
||||
|
||||
} while (true);
|
||||
|
||||
if (err == -ENOENT) {
|
||||
err = 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
return nsi_errno_to_mid(-err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_readdir(const char *path, void *buf,
|
||||
fuse_fill_dir_t filler, off_t off,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
/* Status shared between readdir_* calls */
|
||||
static struct {
|
||||
struct fs_dir_t dir;
|
||||
struct fs_dirent entry;
|
||||
} readdir_status;
|
||||
|
||||
static int ffa_readdir_start(const char *path)
|
||||
{
|
||||
int err;
|
||||
struct stat stat;
|
||||
|
||||
ARG_UNUSED(off);
|
||||
ARG_UNUSED(fi);
|
||||
|
||||
if (strcmp(path, "/") == 0) {
|
||||
return fuse_fs_access_readmount(buf, filler);
|
||||
}
|
||||
|
||||
fs_dir_t_init(&dir);
|
||||
|
||||
if (is_mount_point(path)) {
|
||||
/* File system API expects trailing slash for a mount point
|
||||
* directory but FUSE strips the trailing slashes from
|
||||
* directory names so add it back.
|
||||
*/
|
||||
char mount_path[PATH_MAX] = {0};
|
||||
size_t len = strlen(path);
|
||||
|
||||
if (len >= (PATH_MAX - 2)) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(mount_path, path, len);
|
||||
mount_path[len] = '/';
|
||||
err = fs_opendir(&dir, mount_path);
|
||||
} else {
|
||||
err = fs_opendir(&dir, path);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
stat.st_dev = 0;
|
||||
stat.st_ino = 0;
|
||||
stat.st_nlink = 0;
|
||||
stat.st_uid = getuid();
|
||||
stat.st_gid = getgid();
|
||||
stat.st_rdev = 0;
|
||||
stat.st_atime = 0;
|
||||
stat.st_mtime = 0;
|
||||
stat.st_ctime = 0;
|
||||
stat.st_mode = S_IFDIR | S_IRWX_DIR;
|
||||
stat.st_size = 0;
|
||||
stat.st_blksize = 0;
|
||||
stat.st_blocks = 0;
|
||||
|
||||
filler(buf, ".", &stat, 0);
|
||||
filler(buf, "..", &stat, 0);
|
||||
|
||||
do {
|
||||
err = fs_readdir(&dir, &entry);
|
||||
if (err) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (entry.name[0] == DIR_END) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (entry.type == FS_DIR_ENTRY_DIR) {
|
||||
stat.st_mode = S_IFDIR | S_IRWX_DIR;
|
||||
stat.st_size = 0;
|
||||
} else {
|
||||
stat.st_mode = S_IFREG | S_IRW_FILE;
|
||||
stat.st_size = entry.size;
|
||||
}
|
||||
|
||||
if (filler(buf, entry.name, &stat, 0)) {
|
||||
break;
|
||||
}
|
||||
|
||||
} while (1);
|
||||
|
||||
fs_closedir(&dir);
|
||||
|
||||
return err;
|
||||
fs_dir_t_init(&readdir_status.dir);
|
||||
err = fs_opendir(&readdir_status.dir, path);
|
||||
|
||||
return nsi_errno_to_mid(-err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_create(const char *path, mode_t mode,
|
||||
struct fuse_file_info *fi)
|
||||
static int ffa_readdir_read_next(struct ffa_dirent *entry_bottom)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = fs_readdir(&readdir_status.dir, &readdir_status.entry);
|
||||
|
||||
if (err) {
|
||||
return nsi_errno_to_mid(-err);
|
||||
}
|
||||
entry_bottom->name = readdir_status.entry.name;
|
||||
entry_bottom->size = readdir_status.entry.size;
|
||||
|
||||
if (readdir_status.entry.type == FS_DIR_ENTRY_DIR) {
|
||||
entry_bottom->is_directory = true;
|
||||
} else {
|
||||
entry_bottom->is_directory = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ffa_readdir_end(void)
|
||||
{
|
||||
(void)fs_closedir(&readdir_status.dir);
|
||||
}
|
||||
|
||||
static int ffa_create_top(const char *path, uint64_t *fh)
|
||||
{
|
||||
int err;
|
||||
ssize_t handle;
|
||||
|
||||
ARG_UNUSED(mode);
|
||||
|
||||
if (is_mount_point(path)) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
handle = get_new_file_handle();
|
||||
if (handle < 0) {
|
||||
return handle;
|
||||
return nsi_errno_to_mid(-handle);
|
||||
}
|
||||
|
||||
fi->fh = handle;
|
||||
*fh = handle;
|
||||
|
||||
err = fs_open(&files[handle], path, FS_O_CREATE | FS_O_WRITE);
|
||||
if (err != 0) {
|
||||
release_file_handle(handle);
|
||||
fi->fh = INVALID_FILE_HANDLE;
|
||||
return err;
|
||||
*fh = INVALID_FILE_HANDLE;
|
||||
return nsi_errno_to_mid(-err);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fuse_fs_access_open(const char *path, struct fuse_file_info *fi)
|
||||
static int ffa_release_top(uint64_t fh)
|
||||
{
|
||||
return fuse_fs_access_create(path, 0, fi);
|
||||
}
|
||||
fs_close(&files[fh]);
|
||||
|
||||
static int fuse_fs_access_release(const char *path, struct fuse_file_info *fi)
|
||||
{
|
||||
ARG_UNUSED(path);
|
||||
|
||||
if (fi->fh == INVALID_FILE_HANDLE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fs_close(&files[fi->fh]);
|
||||
|
||||
release_file_handle(fi->fh);
|
||||
release_file_handle(fh);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fuse_fs_access_read(const char *path, char *buf, size_t size,
|
||||
off_t off, struct fuse_file_info *fi)
|
||||
static int ffa_read_top(uint64_t fh, char *buf, size_t size, off_t off)
|
||||
{
|
||||
int err;
|
||||
|
||||
ARG_UNUSED(path);
|
||||
err = fs_seek(&files[fh], off, FS_SEEK_SET);
|
||||
|
||||
if (fi->fh == INVALID_FILE_HANDLE) {
|
||||
return -EINVAL;
|
||||
if (err == 0) {
|
||||
err = fs_read(&files[fh], buf, size);
|
||||
}
|
||||
|
||||
err = fs_seek(&files[fi->fh], off, FS_SEEK_SET);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = fs_read(&files[fi->fh], buf, size);
|
||||
|
||||
return err;
|
||||
return nsi_errno_to_mid(-err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_write(const char *path, const char *buf, size_t size,
|
||||
off_t off, struct fuse_file_info *fi)
|
||||
static int ffa_write_top(uint64_t fh, const char *buf, size_t size, off_t off)
|
||||
{
|
||||
int err;
|
||||
|
||||
ARG_UNUSED(path);
|
||||
err = fs_seek(&files[fh], off, FS_SEEK_SET);
|
||||
|
||||
if (fi->fh == INVALID_FILE_HANDLE) {
|
||||
return -EINVAL;
|
||||
if (err == 0) {
|
||||
err = fs_write(&files[fh], buf, size);
|
||||
}
|
||||
|
||||
err = fs_seek(&files[fi->fh], off, FS_SEEK_SET);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = fs_write(&files[fi->fh], buf, size);
|
||||
|
||||
return err;
|
||||
return nsi_errno_to_mid(-err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_ftruncate(const char *path, off_t size,
|
||||
struct fuse_file_info *fi)
|
||||
static int ffa_ftruncate_top(uint64_t fh, off_t size)
|
||||
{
|
||||
int err;
|
||||
int err = fs_truncate(&files[fh], size);
|
||||
|
||||
ARG_UNUSED(path);
|
||||
|
||||
if (fi->fh == INVALID_FILE_HANDLE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = fs_truncate(&files[fi->fh], size);
|
||||
|
||||
return err;
|
||||
return nsi_errno_to_mid(-err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_truncate(const char *path, off_t size)
|
||||
static int ffa_truncate_top(const char *path, off_t size)
|
||||
{
|
||||
int err;
|
||||
static struct fs_file_t file;
|
||||
|
||||
err = fs_open(&file, path, FS_O_CREATE | FS_O_WRITE);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
return nsi_errno_to_mid(-err);
|
||||
}
|
||||
|
||||
err = fs_truncate(&file, size);
|
||||
if (err != 0) {
|
||||
fs_close(&file);
|
||||
return err;
|
||||
} else {
|
||||
err = fs_close(&file);
|
||||
}
|
||||
|
||||
err = fs_close(&file);
|
||||
|
||||
return err;
|
||||
return nsi_errno_to_mid(-err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_mkdir(const char *path, mode_t mode)
|
||||
static int ffa_mkdir_top(const char *path)
|
||||
{
|
||||
ARG_UNUSED(mode);
|
||||
int err = fs_mkdir(path);
|
||||
|
||||
return fs_mkdir(path);
|
||||
return nsi_errno_to_mid(-err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_rmdir(const char *path)
|
||||
static int ffa_unlink_top(const char *path)
|
||||
{
|
||||
return fs_unlink(path);
|
||||
int err = fs_unlink(path);
|
||||
|
||||
return nsi_errno_to_mid(-err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_unlink(const char *path)
|
||||
{
|
||||
return fs_unlink(path);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_statfs(const char *path, struct statvfs *buf)
|
||||
{
|
||||
ARG_UNUSED(path);
|
||||
ARG_UNUSED(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fuse_fs_access_utimens(const char *path, const struct timespec tv[2])
|
||||
{
|
||||
/* dummy */
|
||||
ARG_UNUSED(path);
|
||||
ARG_UNUSED(tv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct fuse_operations fuse_fs_access_oper = {
|
||||
.getattr = fuse_fs_access_getattr,
|
||||
.readlink = NULL,
|
||||
.getdir = NULL,
|
||||
.mknod = NULL,
|
||||
.mkdir = fuse_fs_access_mkdir,
|
||||
.unlink = fuse_fs_access_unlink,
|
||||
.rmdir = fuse_fs_access_rmdir,
|
||||
.symlink = NULL,
|
||||
.rename = NULL,
|
||||
.link = NULL,
|
||||
.chmod = NULL,
|
||||
.chown = NULL,
|
||||
.truncate = fuse_fs_access_truncate,
|
||||
.utime = NULL,
|
||||
.open = fuse_fs_access_open,
|
||||
.read = fuse_fs_access_read,
|
||||
.write = fuse_fs_access_write,
|
||||
.statfs = fuse_fs_access_statfs,
|
||||
.flush = NULL,
|
||||
.release = fuse_fs_access_release,
|
||||
.fsync = NULL,
|
||||
.setxattr = NULL,
|
||||
.getxattr = NULL,
|
||||
.listxattr = NULL,
|
||||
.removexattr = NULL,
|
||||
.opendir = NULL,
|
||||
.readdir = fuse_fs_access_readdir,
|
||||
.releasedir = NULL,
|
||||
.fsyncdir = NULL,
|
||||
.init = NULL,
|
||||
.destroy = NULL,
|
||||
.access = NULL,
|
||||
.create = fuse_fs_access_create,
|
||||
.ftruncate = fuse_fs_access_ftruncate,
|
||||
.fgetattr = NULL,
|
||||
.lock = NULL,
|
||||
.utimens = fuse_fs_access_utimens,
|
||||
.bmap = NULL,
|
||||
.flag_nullpath_ok = 0,
|
||||
.flag_nopath = 0,
|
||||
.flag_utime_omit_ok = 0,
|
||||
.flag_reserved = 0,
|
||||
.ioctl = NULL,
|
||||
.poll = NULL,
|
||||
.write_buf = NULL,
|
||||
.read_buf = NULL,
|
||||
.flock = NULL,
|
||||
.fallocate = NULL,
|
||||
struct ffa_op_callbacks op_callbacks = {
|
||||
.readdir_start = ffa_readdir_start,
|
||||
.readdir_read_next = ffa_readdir_read_next,
|
||||
.readdir_end = ffa_readdir_end,
|
||||
.stat = ffa_stat_top,
|
||||
.readmount = ffa_readmount_top,
|
||||
.mkdir = ffa_mkdir_top,
|
||||
.create = ffa_create_top,
|
||||
.release = ffa_release_top,
|
||||
.read = ffa_read_top,
|
||||
.write = ffa_write_top,
|
||||
.ftruncate = ffa_ftruncate_top,
|
||||
.truncate = ffa_truncate_top,
|
||||
.unlink = ffa_unlink_top,
|
||||
.rmdir = ffa_unlink_top,
|
||||
};
|
||||
|
||||
static void *fuse_fs_access_main(void *arg)
|
||||
static void fuse_top_dispath_thread(void *arg1, void *arg2, void *arg3)
|
||||
{
|
||||
ARG_UNUSED(arg);
|
||||
ARG_UNUSED(arg1);
|
||||
ARG_UNUSED(arg2);
|
||||
ARG_UNUSED(arg3);
|
||||
|
||||
char *argv[] = {
|
||||
"",
|
||||
"-f",
|
||||
"-s",
|
||||
(char *) fuse_mountpoint
|
||||
};
|
||||
int argc = ARRAY_SIZE(argv);
|
||||
#define COOLDOWN_TIME 10
|
||||
int cooldown_count = 0;
|
||||
|
||||
posix_print_trace("Mounting flash at %s/\n", fuse_mountpoint);
|
||||
fuse_main(argc, argv, &fuse_fs_access_oper, NULL);
|
||||
|
||||
pthread_exit(0);
|
||||
while (true) {
|
||||
if (ffa_is_op_pended()) {
|
||||
ffa_run_pending_op();
|
||||
cooldown_count = COOLDOWN_TIME;
|
||||
} else {
|
||||
if (cooldown_count > 0) {
|
||||
k_sleep(K_MSEC(1));
|
||||
cooldown_count--;
|
||||
} else {
|
||||
k_sleep(K_MSEC(20));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
K_THREAD_DEFINE(fuse_op_handler, CONFIG_ARCH_POSIX_RECOMMENDED_STACK_SIZE,
|
||||
fuse_top_dispath_thread, NULL, NULL, NULL, 100, 0, 0);
|
||||
|
||||
static void fuse_fs_access_init(void)
|
||||
{
|
||||
int err;
|
||||
struct stat st;
|
||||
size_t i = 0;
|
||||
|
||||
while (i < ARRAY_SIZE(files)) {
|
||||
@ -498,44 +270,16 @@ static void fuse_fs_access_init(void)
|
||||
fuse_mountpoint = default_fuse_mountpoint;
|
||||
}
|
||||
|
||||
if (stat(fuse_mountpoint, &st) < 0) {
|
||||
if (mkdir(fuse_mountpoint, 0700) < 0) {
|
||||
posix_print_error_and_exit("Failed to create"
|
||||
" directory for flash mount point (%s): %s\n",
|
||||
fuse_mountpoint, strerror(errno));
|
||||
}
|
||||
} else if (!S_ISDIR(st.st_mode)) {
|
||||
posix_print_error_and_exit("%s is not a directory\n",
|
||||
fuse_mountpoint);
|
||||
|
||||
}
|
||||
|
||||
err = pthread_create(&fuse_thread, NULL, fuse_fs_access_main, NULL);
|
||||
if (err < 0) {
|
||||
posix_print_error_and_exit(
|
||||
"Failed to create thread for "
|
||||
"fuse_fs_access_main\n");
|
||||
}
|
||||
ffsa_init_bottom(fuse_mountpoint, &op_callbacks);
|
||||
}
|
||||
|
||||
static void fuse_fs_access_exit(void)
|
||||
static void fuse_fs_access_cleanup(void)
|
||||
{
|
||||
char *full_cmd;
|
||||
const char cmd[] = "fusermount -uz ";
|
||||
|
||||
if (fuse_mountpoint == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
full_cmd = malloc(strlen(cmd) + strlen(fuse_mountpoint) + 1);
|
||||
|
||||
sprintf(full_cmd, "%s%s", cmd, fuse_mountpoint);
|
||||
if (system(full_cmd) < -1) {
|
||||
printf("Failed to unmount fuse mount point\n");
|
||||
}
|
||||
free(full_cmd);
|
||||
|
||||
pthread_join(fuse_thread, NULL);
|
||||
ffsa_cleanup_bottom(fuse_mountpoint);
|
||||
}
|
||||
|
||||
static void fuse_fs_access_options(void)
|
||||
@ -558,4 +302,4 @@ static void fuse_fs_access_options(void)
|
||||
|
||||
NATIVE_TASK(fuse_fs_access_options, PRE_BOOT_1, 1);
|
||||
NATIVE_TASK(fuse_fs_access_init, PRE_BOOT_2, 1);
|
||||
NATIVE_TASK(fuse_fs_access_exit, ON_EXIT, 1);
|
||||
NATIVE_TASK(fuse_fs_access_cleanup, ON_EXIT, 1);
|
||||
|
||||
642
subsys/fs/fuse_fs_access_bottom.c
Normal file
642
subsys/fs/fuse_fs_access_bottom.c
Normal file
@ -0,0 +1,642 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
|
||||
* Copyright (c) 2025 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define FUSE_USE_VERSION 26
|
||||
|
||||
#undef _XOPEN_SOURCE
|
||||
#define _XOPEN_SOURCE 700
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <fuse.h>
|
||||
#include <libgen.h>
|
||||
#include <linux/limits.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <nsi_tracing.h>
|
||||
#include <nsi_utils.h>
|
||||
#include <nsi_errno.h>
|
||||
#include "fuse_fs_access_bottom.h"
|
||||
|
||||
|
||||
#define S_IRWX_DIR (0775)
|
||||
#define S_IRW_FILE (0664)
|
||||
|
||||
#define DIR_END '\0'
|
||||
|
||||
static pthread_t fuse_thread;
|
||||
static struct ffa_op_callbacks *op_callbacks;
|
||||
|
||||
/* Pending operation the bottom/fuse thread is queuing into the Zephyr thread */
|
||||
struct {
|
||||
int op; /* One of OP_**/
|
||||
void *args; /* Pointer to arguments structure, one of op_args_* or a simple argument */
|
||||
int ret; /* Return from the operation */
|
||||
bool pending; /* Is there a pending operation */
|
||||
sem_t op_done; /* semaphore to signal the job is done */
|
||||
} op_queue;
|
||||
|
||||
#define OP_STAT offsetof(struct ffa_op_callbacks, stat)
|
||||
#define OP_READMOUNT offsetof(struct ffa_op_callbacks, readmount)
|
||||
#define OP_READDIR_START offsetof(struct ffa_op_callbacks, readdir_start)
|
||||
#define OP_READDIR_READ_NEXT offsetof(struct ffa_op_callbacks, readdir_read_next)
|
||||
#define OP_READDIR_END offsetof(struct ffa_op_callbacks, readdir_end)
|
||||
#define OP_MKDIR offsetof(struct ffa_op_callbacks, mkdir)
|
||||
#define OP_CREATE offsetof(struct ffa_op_callbacks, create)
|
||||
#define OP_RELEASE offsetof(struct ffa_op_callbacks, release)
|
||||
#define OP_READ offsetof(struct ffa_op_callbacks, read)
|
||||
#define OP_WRITE offsetof(struct ffa_op_callbacks, write)
|
||||
#define OP_FTRUNCATE offsetof(struct ffa_op_callbacks, ftruncate)
|
||||
#define OP_TRUNCATE offsetof(struct ffa_op_callbacks, truncate)
|
||||
#define OP_UNLINK offsetof(struct ffa_op_callbacks, unlink)
|
||||
#define OP_RMDIR offsetof(struct ffa_op_callbacks, rmdir)
|
||||
|
||||
struct op_args_truncate {
|
||||
const char *path;
|
||||
off_t size;
|
||||
};
|
||||
struct op_args_ftruncate {
|
||||
uint64_t fh;
|
||||
off_t size;
|
||||
};
|
||||
struct op_args_readwrite {
|
||||
uint64_t fh;
|
||||
char *buf;
|
||||
off_t size;
|
||||
off_t off;
|
||||
};
|
||||
struct op_args_create {
|
||||
const char *path;
|
||||
uint64_t *fh_p;
|
||||
};
|
||||
struct op_args_readmount {
|
||||
int *mnt_nbr_p;
|
||||
const char **mnt_name_p;
|
||||
};
|
||||
struct op_args_stat {
|
||||
const char *path;
|
||||
struct ffa_dirent *entry_p;
|
||||
};
|
||||
|
||||
static inline int queue_op(int op, void *args)
|
||||
{
|
||||
op_queue.op = op;
|
||||
op_queue.args = args;
|
||||
op_queue.pending = true;
|
||||
|
||||
sem_wait(&op_queue.op_done);
|
||||
|
||||
return op_queue.ret;
|
||||
}
|
||||
|
||||
bool ffa_is_op_pended(void)
|
||||
{
|
||||
return op_queue.pending;
|
||||
}
|
||||
|
||||
void ffa_run_pending_op(void)
|
||||
{
|
||||
switch ((intptr_t)op_queue.op) {
|
||||
case OP_RMDIR:
|
||||
op_queue.ret = op_callbacks->rmdir((const char *)op_queue.args);
|
||||
break;
|
||||
case OP_UNLINK:
|
||||
op_queue.ret = op_callbacks->unlink((const char *)op_queue.args);
|
||||
break;
|
||||
case OP_TRUNCATE: {
|
||||
struct op_args_truncate *args = op_queue.args;
|
||||
|
||||
op_queue.ret = op_callbacks->truncate(args->path, args->size);
|
||||
break;
|
||||
}
|
||||
case OP_FTRUNCATE: {
|
||||
struct op_args_ftruncate *args = op_queue.args;
|
||||
|
||||
op_queue.ret = op_callbacks->ftruncate(args->fh, args->size);
|
||||
break;
|
||||
}
|
||||
case OP_WRITE: {
|
||||
struct op_args_readwrite *args = op_queue.args;
|
||||
|
||||
op_queue.ret = op_callbacks->write(args->fh, args->buf, args->size, args->off);
|
||||
break;
|
||||
}
|
||||
case OP_READ: {
|
||||
struct op_args_readwrite *args = op_queue.args;
|
||||
|
||||
op_queue.ret = op_callbacks->read(args->fh, args->buf, args->size, args->off);
|
||||
break;
|
||||
}
|
||||
case OP_RELEASE:
|
||||
op_queue.ret = op_callbacks->release(*(uint64_t *)op_queue.args);
|
||||
break;
|
||||
case OP_CREATE: {
|
||||
struct op_args_create *args = op_queue.args;
|
||||
|
||||
op_queue.ret = op_callbacks->create(args->path, args->fh_p);
|
||||
break;
|
||||
}
|
||||
case OP_MKDIR:
|
||||
op_queue.ret = op_callbacks->mkdir((const char *)op_queue.args);
|
||||
break;
|
||||
case OP_READDIR_END:
|
||||
op_callbacks->readdir_end();
|
||||
break;
|
||||
case OP_READDIR_READ_NEXT:
|
||||
op_queue.ret = op_callbacks->readdir_read_next((struct ffa_dirent *)op_queue.args);
|
||||
break;
|
||||
case OP_READDIR_START:
|
||||
op_queue.ret = op_callbacks->readdir_start((const char *)op_queue.args);
|
||||
break;
|
||||
case OP_READMOUNT: {
|
||||
struct op_args_readmount *args = op_queue.args;
|
||||
|
||||
op_queue.ret = op_callbacks->readmount(args->mnt_nbr_p, args->mnt_name_p);
|
||||
break;
|
||||
}
|
||||
case OP_STAT: {
|
||||
struct op_args_stat *args = op_queue.args;
|
||||
|
||||
op_queue.ret = op_callbacks->stat(args->path, args->entry_p);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
nsi_print_error_and_exit("Programming error, unknown queued operation\n");
|
||||
break;
|
||||
}
|
||||
op_queue.pending = false;
|
||||
sem_post(&op_queue.op_done);
|
||||
}
|
||||
|
||||
static bool is_mount_point(const char *path)
|
||||
{
|
||||
char dir_path[PATH_MAX];
|
||||
size_t len;
|
||||
|
||||
len = strlen(path);
|
||||
if (len >= sizeof(dir_path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(dir_path, path, len);
|
||||
dir_path[len] = '\0';
|
||||
return strcmp(dirname(dir_path), "/") == 0;
|
||||
}
|
||||
|
||||
static int fuse_fs_access_getattr(const char *path, struct stat *st)
|
||||
{
|
||||
struct ffa_dirent entry;
|
||||
int err;
|
||||
|
||||
st->st_dev = 0;
|
||||
st->st_ino = 0;
|
||||
st->st_nlink = 0;
|
||||
st->st_uid = getuid();
|
||||
st->st_gid = getgid();
|
||||
st->st_rdev = 0;
|
||||
st->st_blksize = 0;
|
||||
st->st_blocks = 0;
|
||||
st->st_atime = 0;
|
||||
st->st_mtime = 0;
|
||||
st->st_ctime = 0;
|
||||
|
||||
if ((strcmp(path, "/") == 0) || is_mount_point(path)) {
|
||||
if (strstr(path, "/.") != NULL) {
|
||||
return -ENOENT;
|
||||
}
|
||||
st->st_mode = S_IFDIR | S_IRWX_DIR;
|
||||
st->st_size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct op_args_stat args;
|
||||
|
||||
args.path = path;
|
||||
args.entry_p = &entry;
|
||||
|
||||
err = queue_op(OP_STAT, (void *)&args);
|
||||
|
||||
if (err != 0) {
|
||||
return -nsi_errno_from_mid(err);
|
||||
}
|
||||
|
||||
if (entry.is_directory) {
|
||||
st->st_mode = S_IFDIR | S_IRWX_DIR;
|
||||
st->st_size = 0;
|
||||
} else {
|
||||
st->st_mode = S_IFREG | S_IRW_FILE;
|
||||
st->st_size = entry.size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fuse_fs_access_readmount(void *buf, fuse_fill_dir_t filler)
|
||||
{
|
||||
int mnt_nbr = 0;
|
||||
const char *mnt_name;
|
||||
struct stat st;
|
||||
int err;
|
||||
|
||||
st.st_dev = 0;
|
||||
st.st_ino = 0;
|
||||
st.st_nlink = 0;
|
||||
st.st_uid = getuid();
|
||||
st.st_gid = getgid();
|
||||
st.st_rdev = 0;
|
||||
st.st_atime = 0;
|
||||
st.st_mtime = 0;
|
||||
st.st_ctime = 0;
|
||||
st.st_mode = S_IFDIR | S_IRWX_DIR;
|
||||
st.st_size = 0;
|
||||
st.st_blksize = 0;
|
||||
st.st_blocks = 0;
|
||||
|
||||
filler(buf, ".", &st, 0);
|
||||
filler(buf, "..", NULL, 0);
|
||||
|
||||
do {
|
||||
struct op_args_readmount args;
|
||||
|
||||
args.mnt_nbr_p = &mnt_nbr;
|
||||
args.mnt_name_p = &mnt_name;
|
||||
|
||||
err = queue_op(OP_READMOUNT, (void *)&args);
|
||||
err = -nsi_errno_from_mid(err);
|
||||
|
||||
if (err < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
filler(buf, &mnt_name[1], &st, 0);
|
||||
|
||||
} while (true);
|
||||
|
||||
if (err == -ENOENT) {
|
||||
err = 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int fuse_fs_access_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t off,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
NSI_ARG_UNUSED(off);
|
||||
NSI_ARG_UNUSED(fi);
|
||||
|
||||
struct ffa_dirent entry;
|
||||
int err;
|
||||
struct stat st;
|
||||
|
||||
if (strcmp(path, "/") == 0) {
|
||||
err = fuse_fs_access_readmount(buf, filler);
|
||||
return -nsi_errno_from_mid(err);
|
||||
}
|
||||
|
||||
if (is_mount_point(path)) {
|
||||
/* File system API expects trailing slash for a mount point
|
||||
* directory but FUSE strips the trailing slashes from
|
||||
* directory names so add it back.
|
||||
*/
|
||||
char mount_path[PATH_MAX] = {0};
|
||||
size_t len = strlen(path);
|
||||
|
||||
if (len >= (PATH_MAX - 2)) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(mount_path, path, len);
|
||||
mount_path[len] = '/';
|
||||
err = queue_op(OP_READDIR_START, (void *)mount_path);
|
||||
} else {
|
||||
err = queue_op(OP_READDIR_START, (void *)path);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
st.st_dev = 0;
|
||||
st.st_ino = 0;
|
||||
st.st_nlink = 0;
|
||||
st.st_uid = getuid();
|
||||
st.st_gid = getgid();
|
||||
st.st_rdev = 0;
|
||||
st.st_atime = 0;
|
||||
st.st_mtime = 0;
|
||||
st.st_ctime = 0;
|
||||
st.st_mode = S_IFDIR | S_IRWX_DIR;
|
||||
st.st_size = 0;
|
||||
st.st_blksize = 0;
|
||||
st.st_blocks = 0;
|
||||
|
||||
filler(buf, ".", &st, 0);
|
||||
filler(buf, "..", &st, 0);
|
||||
|
||||
do {
|
||||
err = queue_op(OP_READDIR_READ_NEXT, (void *)&entry);
|
||||
if (err) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (entry.name[0] == DIR_END) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (entry.is_directory) {
|
||||
st.st_mode = S_IFDIR | S_IRWX_DIR;
|
||||
st.st_size = 0;
|
||||
} else {
|
||||
st.st_mode = S_IFREG | S_IRW_FILE;
|
||||
st.st_size = entry.size;
|
||||
}
|
||||
|
||||
if (filler(buf, entry.name, &st, 0)) {
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
queue_op(OP_READDIR_END, NULL);
|
||||
|
||||
return -nsi_errno_from_mid(err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_mkdir(const char *path, mode_t mode)
|
||||
{
|
||||
NSI_ARG_UNUSED(mode);
|
||||
|
||||
int err = queue_op(OP_MKDIR, (void *)path);
|
||||
|
||||
return -nsi_errno_from_mid(err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_create(const char *path, mode_t mode, struct fuse_file_info *fi)
|
||||
{
|
||||
int err;
|
||||
struct op_args_create args;
|
||||
|
||||
NSI_ARG_UNUSED(mode);
|
||||
|
||||
if (is_mount_point(path)) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
args.path = path;
|
||||
args.fh_p = &fi->fh;
|
||||
|
||||
err = queue_op(OP_CREATE, (void *)&args);
|
||||
|
||||
return -nsi_errno_from_mid(err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_open(const char *path, struct fuse_file_info *fi)
|
||||
{
|
||||
int err = fuse_fs_access_create(path, 0, fi);
|
||||
|
||||
return -nsi_errno_from_mid(err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_release(const char *path, struct fuse_file_info *fi)
|
||||
{
|
||||
NSI_ARG_UNUSED(path);
|
||||
|
||||
if (fi->fh == INVALID_FILE_HANDLE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
(void)queue_op(OP_RELEASE, (void *)&fi->fh);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fuse_fs_access_read(const char *path, char *buf, size_t size, off_t off,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
int err;
|
||||
struct op_args_readwrite args;
|
||||
|
||||
NSI_ARG_UNUSED(path);
|
||||
|
||||
if (fi->fh == INVALID_FILE_HANDLE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
args.fh = fi->fh;
|
||||
args.buf = buf;
|
||||
args.size = size;
|
||||
args.off = off;
|
||||
|
||||
err = queue_op(OP_READ, (void *)&args);
|
||||
|
||||
return -nsi_errno_from_mid(err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_write(const char *path, const char *buf, size_t size, off_t off,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
int err;
|
||||
struct op_args_readwrite args;
|
||||
|
||||
NSI_ARG_UNUSED(path);
|
||||
|
||||
if (fi->fh == INVALID_FILE_HANDLE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
args.fh = fi->fh;
|
||||
args.buf = (char *)buf;
|
||||
args.size = size;
|
||||
args.off = off;
|
||||
|
||||
err = queue_op(OP_WRITE, (void *)&args);
|
||||
|
||||
return -nsi_errno_from_mid(err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_ftruncate(const char *path, off_t size, struct fuse_file_info *fi)
|
||||
{
|
||||
struct op_args_ftruncate args;
|
||||
int err;
|
||||
|
||||
NSI_ARG_UNUSED(path);
|
||||
|
||||
if (fi->fh == INVALID_FILE_HANDLE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
args.fh = fi->fh;
|
||||
args.size = size;
|
||||
|
||||
err = queue_op(OP_FTRUNCATE, (void *)&args);
|
||||
|
||||
return -nsi_errno_from_mid(err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_truncate(const char *path, off_t size)
|
||||
{
|
||||
struct op_args_truncate args;
|
||||
int err;
|
||||
|
||||
args.path = path;
|
||||
args.size = size;
|
||||
|
||||
err = queue_op(OP_TRUNCATE, (void *)&args);
|
||||
|
||||
return -nsi_errno_from_mid(err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_rmdir(const char *path)
|
||||
{
|
||||
int err = queue_op(OP_RMDIR, (void *)path);
|
||||
|
||||
return -nsi_errno_from_mid(err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_unlink(const char *path)
|
||||
{
|
||||
int err = queue_op(OP_UNLINK, (void *)path);
|
||||
|
||||
return -nsi_errno_from_mid(err);
|
||||
}
|
||||
|
||||
static int fuse_fs_access_statfs(const char *path, struct statvfs *buf)
|
||||
{
|
||||
NSI_ARG_UNUSED(path);
|
||||
NSI_ARG_UNUSED(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fuse_fs_access_utimens(const char *path, const struct timespec tv[2])
|
||||
{
|
||||
/* dummy */
|
||||
NSI_ARG_UNUSED(path);
|
||||
NSI_ARG_UNUSED(tv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct fuse_operations fuse_fs_access_oper = {
|
||||
.getattr = fuse_fs_access_getattr,
|
||||
.readlink = NULL,
|
||||
.getdir = NULL,
|
||||
.mknod = NULL,
|
||||
.mkdir = fuse_fs_access_mkdir,
|
||||
.unlink = fuse_fs_access_unlink,
|
||||
.rmdir = fuse_fs_access_rmdir,
|
||||
.symlink = NULL,
|
||||
.rename = NULL,
|
||||
.link = NULL,
|
||||
.chmod = NULL,
|
||||
.chown = NULL,
|
||||
.truncate = fuse_fs_access_truncate,
|
||||
.utime = NULL,
|
||||
.open = fuse_fs_access_open,
|
||||
.read = fuse_fs_access_read,
|
||||
.write = fuse_fs_access_write,
|
||||
.statfs = fuse_fs_access_statfs,
|
||||
.flush = NULL,
|
||||
.release = fuse_fs_access_release,
|
||||
.fsync = NULL,
|
||||
.setxattr = NULL,
|
||||
.getxattr = NULL,
|
||||
.listxattr = NULL,
|
||||
.removexattr = NULL,
|
||||
.opendir = NULL,
|
||||
.readdir = fuse_fs_access_readdir,
|
||||
.releasedir = NULL,
|
||||
.fsyncdir = NULL,
|
||||
.init = NULL,
|
||||
.destroy = NULL,
|
||||
.access = NULL,
|
||||
.create = fuse_fs_access_create,
|
||||
.ftruncate = fuse_fs_access_ftruncate,
|
||||
.fgetattr = NULL,
|
||||
.lock = NULL,
|
||||
.utimens = fuse_fs_access_utimens,
|
||||
.bmap = NULL,
|
||||
.flag_nullpath_ok = 0,
|
||||
.flag_nopath = 0,
|
||||
.flag_utime_omit_ok = 0,
|
||||
.flag_reserved = 0,
|
||||
.ioctl = NULL,
|
||||
.poll = NULL,
|
||||
.write_buf = NULL,
|
||||
.read_buf = NULL,
|
||||
.flock = NULL,
|
||||
.fallocate = NULL,
|
||||
};
|
||||
|
||||
static void *ffsa_main(void *fuse_mountpoint)
|
||||
{
|
||||
char *argv[] = {
|
||||
"",
|
||||
"-f",
|
||||
"-s",
|
||||
(char *)fuse_mountpoint
|
||||
};
|
||||
int argc = NSI_ARRAY_SIZE(argv);
|
||||
|
||||
nsi_print_trace("FUSE mounting flash in host %s/\n", (char *)fuse_mountpoint);
|
||||
|
||||
fuse_main(argc, argv, &fuse_fs_access_oper, NULL);
|
||||
|
||||
pthread_exit(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ffsa_init_bottom(const char *fuse_mountpoint, struct ffa_op_callbacks *op_cbs)
|
||||
{
|
||||
struct stat st;
|
||||
int err;
|
||||
|
||||
op_callbacks = op_cbs;
|
||||
|
||||
if (stat(fuse_mountpoint, &st) < 0) {
|
||||
if (mkdir(fuse_mountpoint, 0700) < 0) {
|
||||
nsi_print_error_and_exit("Failed to create directory for flash mount point "
|
||||
"(%s): %s\n",
|
||||
fuse_mountpoint, strerror(errno));
|
||||
}
|
||||
} else if (!S_ISDIR(st.st_mode)) {
|
||||
nsi_print_error_and_exit("%s is not a directory\n", fuse_mountpoint);
|
||||
}
|
||||
|
||||
err = pthread_create(&fuse_thread, NULL, ffsa_main, (void *)fuse_mountpoint);
|
||||
if (err < 0) {
|
||||
nsi_print_error_and_exit("Failed to create thread for fuse_fs_access_main\n");
|
||||
}
|
||||
|
||||
err = sem_init(&op_queue.op_done, 0, 0);
|
||||
if (err) {
|
||||
nsi_print_error_and_exit("Failed to initialize semaphore\n");
|
||||
}
|
||||
}
|
||||
|
||||
void ffsa_cleanup_bottom(const char *fuse_mountpoint)
|
||||
{
|
||||
char *full_cmd;
|
||||
static const char cmd[] = "fusermount -uz ";
|
||||
|
||||
full_cmd = malloc(strlen(cmd) + strlen(fuse_mountpoint) + 1);
|
||||
|
||||
sprintf(full_cmd, "%s%s", cmd, fuse_mountpoint);
|
||||
if (system(full_cmd) < -1) {
|
||||
nsi_print_trace("Failed to unmount fuse mount point\n");
|
||||
}
|
||||
free(full_cmd);
|
||||
|
||||
pthread_join(fuse_thread, NULL);
|
||||
}
|
||||
51
subsys/fs/fuse_fs_access_bottom.h
Normal file
51
subsys/fs/fuse_fs_access_bottom.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef SUBSYS_FS_FUSE_FS_ACCESS_BOTTOM_H
|
||||
#define SUBSYS_FS_FUSE_FS_ACCESS_BOTTOM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define INVALID_FILE_HANDLE (INT32_MAX)
|
||||
|
||||
struct ffa_dirent {
|
||||
bool is_directory;
|
||||
char *name;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct ffa_op_callbacks {
|
||||
int (*stat)(const char *path, struct ffa_dirent *entry);
|
||||
int (*readmount)(int *mnt_nbr, const char **mnt_name);
|
||||
int (*readdir_start)(const char *path);
|
||||
int (*readdir_read_next)(struct ffa_dirent *entry);
|
||||
void (*readdir_end)(void);
|
||||
int (*mkdir)(const char *path);
|
||||
int (*create)(const char *path, uint64_t *fh);
|
||||
int (*release)(uint64_t fh);
|
||||
int (*read)(uint64_t fh, char *buf, size_t size, off_t off);
|
||||
int (*write)(uint64_t fh, const char *buf, size_t size, off_t off);
|
||||
int (*ftruncate)(uint64_t fh, off_t size);
|
||||
int (*truncate)(const char *path, off_t size);
|
||||
int (*unlink)(const char *path);
|
||||
int (*rmdir)(const char *path);
|
||||
};
|
||||
|
||||
void ffsa_init_bottom(const char *fuse_mountpoint, struct ffa_op_callbacks *op_cbs);
|
||||
void ffsa_cleanup_bottom(const char *fuse_mountpoint);
|
||||
|
||||
bool ffa_is_op_pended(void);
|
||||
void ffa_run_pending_op(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SUBSYS_FS_FUSE_FS_ACCESS_BOTTOM_H */
|
||||
Loading…
Reference in New Issue
Block a user