From 0db06bc7f1b520d5da3c7310bf0ef45a0a16f5d3 Mon Sep 17 00:00:00 2001 From: Alberto Escolar Piedras Date: Wed, 17 Sep 2025 16:09:50 +0200 Subject: [PATCH 1/4] subsys/fs/fuse: Misc build fixes FUSE_INCLUDE_DIRS should be passed to the native_simulator build. If there is several fuse libraries installed, we need to ensure the bottom side of the driver is built with the correct include paths. (These includes are irrelevant for the Zephyr side build) Support having a list of include paths instead of single one. Support having a list of libraries to link to instead of a single one. Signed-off-by: Alberto Escolar Piedras --- subsys/fs/CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/subsys/fs/CMakeLists.txt b/subsys/fs/CMakeLists.txt index 512389eef7648..5f3f45785dfa0 100644 --- a/subsys/fs/CMakeLists.txt +++ b/subsys/fs/CMakeLists.txt @@ -37,8 +37,10 @@ if(CONFIG_FUSE_FS_ACCESS) zephyr_library_named(FS_FUSE) find_package(PkgConfig REQUIRED) pkg_search_module(FUSE REQUIRED fuse) - zephyr_include_directories(${FUSE_INCLUDE_DIRS}) - target_link_options(native_simulator INTERFACE "-l${FUSE_LIBRARIES}") + list(TRANSFORM FUSE_INCLUDE_DIRS PREPEND "-I") + target_compile_options(native_simulator INTERFACE ${FUSE_INCLUDE_DIRS}) + list(TRANSFORM FUSE_LIBRARIES PREPEND "-l") + target_link_options(native_simulator INTERFACE ${FUSE_LIBRARIES}) target_sources(native_simulator INTERFACE fuse_fs_access_bottom.c) target_compile_options(native_simulator INTERFACE "-D_FILE_OFFSET_BITS=64") zephyr_library_sources(fuse_fs_access.c) From 94040be709644b6dfc4e4c21f9f8f415f4cdb758 Mon Sep 17 00:00:00 2001 From: Alberto Escolar Piedras Date: Thu, 18 Sep 2025 12:00:45 +0200 Subject: [PATCH 2/4] subsys/fs/fuse: Avoid possible race Avoid a possible race between the FUSE thread and the Zephyr threads during init. Signed-off-by: Alberto Escolar Piedras --- subsys/fs/fuse_fs_access_bottom.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/subsys/fs/fuse_fs_access_bottom.c b/subsys/fs/fuse_fs_access_bottom.c index 1bde305d4f04b..067f3b149a5f9 100644 --- a/subsys/fs/fuse_fs_access_bottom.c +++ b/subsys/fs/fuse_fs_access_bottom.c @@ -615,15 +615,15 @@ void ffsa_init_bottom(const char *fuse_mountpoint, struct ffa_op_callbacks *op_c 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"); } + + 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"); + } } void ffsa_cleanup_bottom(const char *fuse_mountpoint) From d3cd72ab3b7d3a3344e5478c4dfac0b9038bf602 Mon Sep 17 00:00:00 2001 From: Alberto Escolar Piedras Date: Wed, 17 Sep 2025 16:13:04 +0200 Subject: [PATCH 3/4] subsys/fs/fuse: Support using libfuse3 Support using FUSE v3, instead of FUSE v2. FUSE v3 has been out for a couple of years now, so most distributions have it. But note the FUSE library (and its v3 branch) is currently unmaintained, and older distributions do not ship it. Some Linux distros have transitioned their packages away from fuse2. Which results in less atention in these distros to fuse2, and therefore a higher likelyhood that there would be distribution issues with the corresponding packages. There is also some likelihood the fuse2 packages may be eventually droped by some of these. So let's support either version, with a kconfig adapting to either API and their quirks. Note that both the fuse2 and fuse3 library code have quite a few sideeffects on the process that uses it. By now we continue defaulting to fuse2 as it is the most common. But users can select to use the v3 if they have an issue w v2 or want to start trying out using v3. Signed-off-by: Alberto Escolar Piedras --- subsys/fs/CMakeLists.txt | 7 +- subsys/fs/Kconfig | 17 +++++ subsys/fs/fuse_fs_access_bottom.c | 104 ++++++++++++++++++++++++++++-- 3 files changed, 121 insertions(+), 7 deletions(-) diff --git a/subsys/fs/CMakeLists.txt b/subsys/fs/CMakeLists.txt index 5f3f45785dfa0..22615fd4cb098 100644 --- a/subsys/fs/CMakeLists.txt +++ b/subsys/fs/CMakeLists.txt @@ -36,7 +36,12 @@ add_subdirectory_ifdef(CONFIG_ZMS ./zms) if(CONFIG_FUSE_FS_ACCESS) zephyr_library_named(FS_FUSE) find_package(PkgConfig REQUIRED) - pkg_search_module(FUSE REQUIRED fuse) + if (CONFIG_FUSE_LIBRARY_V3) + pkg_search_module(FUSE REQUIRED fuse3) + target_compile_options(native_simulator INTERFACE "-DCONFIG_FUSE_LIBRARY_V3") + else() + pkg_search_module(FUSE REQUIRED fuse) + endif() list(TRANSFORM FUSE_INCLUDE_DIRS PREPEND "-I") target_compile_options(native_simulator INTERFACE ${FUSE_INCLUDE_DIRS}) list(TRANSFORM FUSE_LIBRARIES PREPEND "-l") diff --git a/subsys/fs/Kconfig b/subsys/fs/Kconfig index b1e570436db01..0b6a8d96e309a 100644 --- a/subsys/fs/Kconfig +++ b/subsys/fs/Kconfig @@ -106,6 +106,23 @@ config FUSE_FS_ACCESS help Expose file system partitions to the host system through FUSE. +choice FUSE_LIBRARY_VERSION + prompt "Host FUSE library version" + depends on FUSE_FS_ACCESS + default FUSE_LIBRARY_V2 + +config FUSE_LIBRARY_V2 + bool "Use libfuse(2)" + help + Use v2 of the host FUSE library. + +config FUSE_LIBRARY_V3 + bool "Use libfuse3" + help + Use the host fuse3 library. This may not be available in older distributions. + +endchoice + endif # FILE_SYSTEM if FILE_SYSTEM_LIB_LINK diff --git a/subsys/fs/fuse_fs_access_bottom.c b/subsys/fs/fuse_fs_access_bottom.c index 067f3b149a5f9..38a22b7f5d538 100644 --- a/subsys/fs/fuse_fs_access_bottom.c +++ b/subsys/fs/fuse_fs_access_bottom.c @@ -5,7 +5,11 @@ * SPDX-License-Identifier: Apache-2.0 */ +#if defined(CONFIG_FUSE_LIBRARY_V3) +#define FUSE_USE_VERSION 30 +#else #define FUSE_USE_VERSION 26 +#endif #undef _XOPEN_SOURCE #define _XOPEN_SOURCE 700 @@ -40,6 +44,9 @@ static pthread_t fuse_thread; static struct ffa_op_callbacks *op_callbacks; +#if defined(CONFIG_FUSE_LIBRARY_V3) +static sem_t fuse_started; /* semaphore to signal fuse_main has started */ +#endif /* Pending operation the bottom/fuse thread is queuing into the Zephyr thread */ struct { @@ -197,8 +204,14 @@ static bool is_mount_point(const char *path) return strcmp(dirname(dir_path), "/") == 0; } +#if defined(CONFIG_FUSE_LIBRARY_V3) +static int fuse_fs_access_getattr(const char *path, struct stat *st, struct fuse_file_info *fi) +{ + NSI_ARG_UNUSED(fi); +#else static int fuse_fs_access_getattr(const char *path, struct stat *st) { +#endif struct ffa_dirent entry; int err; @@ -266,8 +279,13 @@ static int fuse_fs_access_readmount(void *buf, fuse_fill_dir_t filler) st.st_blksize = 0; st.st_blocks = 0; +#if defined(CONFIG_FUSE_LIBRARY_V3) + filler(buf, ".", &st, 0, 0); + filler(buf, "..", NULL, 0, 0); +#else filler(buf, ".", &st, 0); filler(buf, "..", NULL, 0); +#endif do { struct op_args_readmount args; @@ -282,7 +300,11 @@ static int fuse_fs_access_readmount(void *buf, fuse_fill_dir_t filler) break; } +#if defined(CONFIG_FUSE_LIBRARY_V3) + filler(buf, &mnt_name[1], &st, 0, 0); +#else filler(buf, &mnt_name[1], &st, 0); +#endif } while (true); @@ -293,9 +315,16 @@ static int fuse_fs_access_readmount(void *buf, fuse_fill_dir_t filler) return err; } +#if defined(CONFIG_FUSE_LIBRARY_V3) +static int fuse_fs_access_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t off, + struct fuse_file_info *fi, enum fuse_readdir_flags flags) +{ + NSI_ARG_UNUSED(flags); +#else static int fuse_fs_access_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t off, struct fuse_file_info *fi) { +#endif NSI_ARG_UNUSED(off); NSI_ARG_UNUSED(fi); @@ -345,8 +374,13 @@ static int fuse_fs_access_readdir(const char *path, void *buf, fuse_fill_dir_t f st.st_blksize = 0; st.st_blocks = 0; +#if defined(CONFIG_FUSE_LIBRARY_V3) + filler(buf, ".", &st, 0, 0); + filler(buf, "..", &st, 0, 0); +#else filler(buf, ".", &st, 0); filler(buf, "..", &st, 0); +#endif do { err = queue_op(OP_READDIR_READ_NEXT, (void *)&entry); @@ -366,7 +400,13 @@ static int fuse_fs_access_readdir(const char *path, void *buf, fuse_fill_dir_t f st.st_size = entry.size; } - if (filler(buf, entry.name, &st, 0)) { +#if defined(CONFIG_FUSE_LIBRARY_V3) + bool full = filler(buf, entry.name, &st, 0, 0); +#else + bool full = filler(buf, entry.name, &st, 0); +#endif + + if (full) { break; } } while (1); @@ -468,6 +508,7 @@ static int fuse_fs_access_write(const char *path, const char *buf, size_t size, return -nsi_errno_from_mid(err); } +#if !defined(CONFIG_FUSE_LIBRARY_V3) static int fuse_fs_access_ftruncate(const char *path, off_t size, struct fuse_file_info *fi) { struct op_args_ftruncate args; @@ -486,9 +527,16 @@ static int fuse_fs_access_ftruncate(const char *path, off_t size, struct fuse_fi return -nsi_errno_from_mid(err); } +#endif +#if defined(CONFIG_FUSE_LIBRARY_V3) +static int fuse_fs_access_truncate(const char *path, off_t size, struct fuse_file_info *fi) +{ + NSI_ARG_UNUSED(fi); +#else static int fuse_fs_access_truncate(const char *path, off_t size) { +#endif struct op_args_truncate args; int err; @@ -521,18 +569,35 @@ static int fuse_fs_access_statfs(const char *path, struct statvfs *buf) return 0; } +#if defined(CONFIG_FUSE_LIBRARY_V3) +static int fuse_fs_access_utimens(const char *path, const struct timespec tv[2], + struct fuse_file_info *fi) +{ + NSI_ARG_UNUSED(fi); +#else static int fuse_fs_access_utimens(const char *path, const struct timespec tv[2]) { +#endif /* dummy */ NSI_ARG_UNUSED(path); NSI_ARG_UNUSED(tv); return 0; } +#if defined(CONFIG_FUSE_LIBRARY_V3) +static void *fuse_fs_access_init(struct fuse_conn_info *conn, struct fuse_config *cfg) +{ + NSI_ARG_UNUSED(conn); + NSI_ARG_UNUSED(cfg); + + sem_post(&fuse_started); + return NULL; +} +#endif + 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, @@ -543,7 +608,6 @@ static struct fuse_operations fuse_fs_access_oper = { .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, @@ -559,19 +623,24 @@ static struct fuse_operations fuse_fs_access_oper = { .readdir = fuse_fs_access_readdir, .releasedir = NULL, .fsyncdir = NULL, - .init = NULL, +#if defined(CONFIG_FUSE_LIBRARY_V3) + .init = fuse_fs_access_init, +#endif .destroy = NULL, .access = NULL, .create = fuse_fs_access_create, +#if !defined(CONFIG_FUSE_LIBRARY_V3) .ftruncate = fuse_fs_access_ftruncate, - .fgetattr = NULL, +#endif .lock = NULL, .utimens = fuse_fs_access_utimens, .bmap = NULL, +#if !defined(CONFIG_FUSE_LIBRARY_V3) .flag_nullpath_ok = 0, .flag_nopath = 0, .flag_utime_omit_ok = 0, .flag_reserved = 0, +#endif .ioctl = NULL, .poll = NULL, .write_buf = NULL, @@ -615,6 +684,18 @@ void ffsa_init_bottom(const char *fuse_mountpoint, struct ffa_op_callbacks *op_c nsi_print_error_and_exit("%s is not a directory\n", fuse_mountpoint); } +#if defined(CONFIG_FUSE_LIBRARY_V3) + /* Fuse3's fuse_daemonize() changes the cwd to "/" which is a quite undesirable + * side-effect, so we will revert back to where we were once it has initialized + */ + char *cwd = getcwd(NULL, 0); + + err = sem_init(&fuse_started, 0, 0); + if (err) { + nsi_print_error_and_exit("Failed to initialize semaphore\n"); + } +#endif + err = sem_init(&op_queue.op_done, 0, 0); if (err) { nsi_print_error_and_exit("Failed to initialize semaphore\n"); @@ -622,8 +703,19 @@ void ffsa_init_bottom(const char *fuse_mountpoint, struct ffa_op_callbacks *op_c 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"); + nsi_print_error_and_exit("Failed to create thread for ffsa_main()\n"); + } + +#if defined(CONFIG_FUSE_LIBRARY_V3) + if (cwd != NULL) { + sem_wait(&fuse_started); + if (chdir(cwd)) { + nsi_print_error_and_exit("Failed to change directory back to %s " + "after starting FUSE\n"); + } + free(cwd); } +#endif } void ffsa_cleanup_bottom(const char *fuse_mountpoint) From d39f4e9636fe07749a3ca6ae062e43d101cf0409 Mon Sep 17 00:00:00 2001 From: Alberto Escolar Piedras Date: Thu, 18 Sep 2025 14:51:32 +0200 Subject: [PATCH 4/4] doc: board native_sim: Mention that now we support FUSEv3 also How to select it, and what packages can be used. Signed-off-by: Alberto Escolar Piedras --- boards/native/native_sim/doc/index.rst | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/boards/native/native_sim/doc/index.rst b/boards/native/native_sim/doc/index.rst index 2e2eb74ee67b4..21352259d7891 100644 --- a/boards/native/native_sim/doc/index.rst +++ b/boards/native/native_sim/doc/index.rst @@ -676,21 +676,26 @@ crashes, you can cleanup the stale mount point by using the program $ fusermount -u flash -Note that this feature requires a 32-bit version of the FUSE library, with a -minimal version of 2.6, on the host system and ``pkg-config`` settings to -correctly pickup the FUSE install path and compiler flags. +You can chose to use the v2 FUSE host library or the v3 with +:kconfig:option:`CONFIG_FUSE_LIBRARY_VERSION`. +When using the v2, a minimal version of 2.6 is necessary. For v3, 3.0 should suffice. +You will also need ``pkg-config`` setup to correctly pickup the FUSE install path and compiler flags. +Note that using this feature with the 32-bit native_sim variant requires the 32-bit version of the +corresponding FUSE library. -On a Ubuntu 22.04 host system, for example, install the ``pkg-config`` and -``libfuse-dev:i386`` packages, and configure the pkg-config search path with -these commands: +For example, to use the v2 of the library, on a Ubuntu 24.04 host system, install the ``pkg-config`` +and ``libfuse-dev:i386`` for 32-bit builds, and ``libfuse-dev`` for 64-bit builds: .. code-block:: console $ sudo dpkg --add-architecture i386 $ sudo apt update - $ sudo apt-get install pkg-config libfuse-dev:i386 + $ sudo apt-get install pkg-config libfuse-dev:i386 libfuse-dev $ export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig +Similarly ``libfuse3-dev:i386`` and ``libfuse3-dev`` provide the 32 and 64-bit FUSE v3 library +and headers. + .. _native_sim_peripherals_c_compat: Peripherals and backends C library compatibility