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 diff --git a/subsys/fs/CMakeLists.txt b/subsys/fs/CMakeLists.txt index 512389eef7648..22615fd4cb098 100644 --- a/subsys/fs/CMakeLists.txt +++ b/subsys/fs/CMakeLists.txt @@ -36,9 +36,16 @@ 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) - zephyr_include_directories(${FUSE_INCLUDE_DIRS}) - target_link_options(native_simulator INTERFACE "-l${FUSE_LIBRARIES}") + 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") + 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) 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 1bde305d4f04b..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,15 +684,38 @@ 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"); +#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"); } + + err = pthread_create(&fuse_thread, NULL, ffsa_main, (void *)fuse_mountpoint); + if (err < 0) { + 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)