From 6afa8acaa8489beacf8fe1befe14264f57c2b421 Mon Sep 17 00:00:00 2001 From: Johannes Leupolz Date: Thu, 30 Oct 2025 23:15:57 +0000 Subject: [PATCH] Include relevant subset of code of libfuse-sys that has not been released by the original maintainer. By this opportunity, also remove the high level api of fuse, because we don't need it. Long term goal would be to migrate to a rust port with less unsafe code. Upate build instructions --- Cargo.toml | 1 + cuse-lowlevel/Cargo.toml | 19 +++++++ cuse-lowlevel/README.md | 28 +++++++++ cuse-lowlevel/build.rs | 120 +++++++++++++++++++++++++++++++++++++++ cuse-lowlevel/src/lib.rs | 31 ++++++++++ docs/BUILD.md | 4 +- vuinputd/Cargo.toml | 4 +- vuinputd/src/main.rs | 3 +- 8 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 cuse-lowlevel/Cargo.toml create mode 100644 cuse-lowlevel/README.md create mode 100644 cuse-lowlevel/build.rs create mode 100644 cuse-lowlevel/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 5a7149c..4128079 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "uinput-ioctls", + "cuse-lowlevel", "vuinputd", "vuinput-examples", ] diff --git a/cuse-lowlevel/Cargo.toml b/cuse-lowlevel/Cargo.toml new file mode 100644 index 0000000..074922e --- /dev/null +++ b/cuse-lowlevel/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "cuse-lowlevel" +version = "0.1.0" +edition = "2021" +license = "MIT AND LGPL-2.0-or-later" +description = "Raw bindings to the low level api of cuse and fuse in libfuse3" +repository = "https://github.com/joleuger/vuinputd" +authors = ["Richard Wiedenhöft ", "Johannes Leupolz "] +categories = ["external-ffi-bindings"] +keywords = ["cuse", "fuse", "bindings", "filesystem", "fs"] +readme = "README.md" +build = "build.rs" + +[dependencies] +libc = "0.2" + +[build-dependencies] +bindgen = "0.60" +pkg-config = "0.3" \ No newline at end of file diff --git a/cuse-lowlevel/README.md b/cuse-lowlevel/README.md new file mode 100644 index 0000000..3e2f10f --- /dev/null +++ b/cuse-lowlevel/README.md @@ -0,0 +1,28 @@ +# cuse-lowlevel + +[crates.io]: https://crates.io/crates/cuse-lowlevel + +**Raw bindings to the low level api of cuse and fuse in libfuse3** + +--- + +## About + +This crate is heavily based on libfuse-sys by Richard Wiedenhöft. See the [original repository](https://github.com/richard-w/libfuse-sys) + +This fork here contains only the relevant subset of code of libfuse-sys to access the low-level api of cuse. + +## Using cuse-lowlevel + +Add the dependencies to your Cargo.toml +```toml +[dependencies] +cuse-lowlevel = { version = "0.1"} +libc = "*" +``` + +## License + +This crate itself is published under the MIT license while libfuse is published under +LGPL2+. Take special care to ensure the terms of the LGPL2+ are honored when using this +crate. \ No newline at end of file diff --git a/cuse-lowlevel/build.rs b/cuse-lowlevel/build.rs new file mode 100644 index 0000000..0c45b92 --- /dev/null +++ b/cuse-lowlevel/build.rs @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: MIT +// +// Author: Richard Wiedenhöft +// Author: Johannes Leupolz +// +// This library is heavily baased on https://github.com/richard-w/libfuse-sys +// but adopted to only provide the low-level modules of fuse and cuse. + +extern crate bindgen; +extern crate pkg_config; + +use std::env; +use std::iter; +use std::path::PathBuf; + +const FUSE_USE_VERSION: u32 = 314; //fuse version of ubuntu 24.04 + + +fn fuse_binding_filter(builder: bindgen::Builder) -> bindgen::Builder { + let builder = builder + // Whitelist "fuse_*" symbols and blocklist everything else + .allowlist_recursively(false) + .allowlist_type("(?i)^fuse.*") + .allowlist_function("(?i)^fuse.*") + .allowlist_var("(?i)^fuse.*") + .blocklist_type("fuse_log_func_t") + .blocklist_function("fuse_set_log_func"); + builder +} + +fn cuse_binding_filter(builder: bindgen::Builder) -> bindgen::Builder { + builder + // Whitelist "cuse_*" symbols and blocklist everything else + .allowlist_recursively(false) + .allowlist_type("(?i)^cuse.*") + .allowlist_function("(?i)^cuse.*") + .allowlist_var("(?i)^cuse.*") +} + +fn generate_fuse_bindings( + header: &str, + fuse_lib: &pkg_config::Library, + binding_filter: fn(bindgen::Builder) -> bindgen::Builder, +) { + // Find header file + let mut header_path: Option = None; + for include_path in fuse_lib.include_paths.iter() { + let test_path = include_path.join(header); + if test_path.exists() { + header_path = Some(test_path); + break; + } + } + let header_path = header_path + .unwrap_or_else(|| panic!("Cannot find {}", header)) + .to_str() + .unwrap_or_else(|| panic!("Path to {} contains invalid unicode characters", header)) + .to_string(); + + // Gather fuse defines + let defines = fuse_lib.defines.iter().map(|(key, val)| match val { + Some(val) => format!("-D{}={}", key, val), + None => format!("-D{}", key), + }); + // Gather include paths + let includes = fuse_lib + .include_paths + .iter() + .map(|dir| format!("-I{}", dir.display())); + // API version definition + let api_define = iter::once(format!("-DFUSE_USE_VERSION={}", FUSE_USE_VERSION)); + // Chain compile flags + let compile_flags = defines.chain(includes).chain(api_define); + + // Create bindgen builder + let mut builder = bindgen::builder() + // Add clang flags + .clang_args(compile_flags) + // Derive Debug, Copy and Default + .derive_default(true) + .derive_copy(true) + .derive_debug(true) + // Add CargoCallbacks so build.rs is rerun on header changes + .parse_callbacks(Box::new(bindgen::CargoCallbacks)); + + builder = binding_filter(builder); + + // Generate bindings + let bindings = builder + .header(header_path) + .generate() + .unwrap_or_else(|_| panic!("Failed to generate {} bindings", header)); + + // Write bindings to file + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let bindings_path = out_dir.join(&header.replace(".h", ".rs")); + bindings + .write_to_file(&bindings_path) + .unwrap_or_else(|_| panic!("Failed to write {}", bindings_path.display())); +} + +fn main() { + let mut pkgcfg = pkg_config::Config::new(); + + // Find libfuse + let fuse3_lib = pkgcfg.cargo_metadata(true).probe("fuse3").expect("Failed to find pkg-config module fuse3"); + + // Generate lowlevel bindings + generate_fuse_bindings( + "fuse_lowlevel.h", + &fuse3_lib, + fuse_binding_filter, + ); + // Generate lowlevel cuse bindings + generate_fuse_bindings( + "cuse_lowlevel.h", + &fuse3_lib, + cuse_binding_filter, + ); +} \ No newline at end of file diff --git a/cuse-lowlevel/src/lib.rs b/cuse-lowlevel/src/lib.rs new file mode 100644 index 0000000..5a3b6c1 --- /dev/null +++ b/cuse-lowlevel/src/lib.rs @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +// +// Author: Richard Wiedenhöft +// Author: Johannes Leupolz +// +// This library is heavily baased on https://github.com/richard-w/libfuse-sys +// but adopted to only provide the low-level modules of fuse and cuse. + +#![allow(non_snake_case)] +#![allow(non_camel_case_types)] +#![allow(non_upper_case_globals)] +#![allow(clippy::useless_transmute)] +#![allow(clippy::cognitive_complexity)] +#![allow(clippy::missing_safety_doc)] + +use libc::*; + + +pub mod fuse_lowlevel { + use super::*; + include!(concat!(env!("OUT_DIR"), "/fuse_lowlevel.rs")); +} + +pub mod cuse_lowlevel { + use super::*; + include!(concat!(env!("OUT_DIR"), "/cuse_lowlevel.rs")); + + use fuse_lowlevel::{ + fuse_args, fuse_conn_info, fuse_file_info, fuse_pollhandle, fuse_req_t, fuse_session, + }; +} \ No newline at end of file diff --git a/docs/BUILD.md b/docs/BUILD.md index 774f0ae..96e314f 100644 --- a/docs/BUILD.md +++ b/docs/BUILD.md @@ -3,7 +3,7 @@ ## 🔹 Prerequisites * Rust toolchain (recommended: install via [rustup](https://rustup.rs)) -* Linux with `libfuse3-dev`, `libudev-dev` and `pkg-config` installed (for cuse/udev access) +* Linux with `build-essential`, `libc6-dev`, `libfuse3-dev`, `libudev-dev` and `pkg-config` installed (for cuse/udev access) --- @@ -19,7 +19,7 @@ cd vuinputd Build all crates (daemon, forwarder, announce, common): ```bash -apt-get install libfuse3-dev pkg-config fuse3 libudev-dev +apt-get install build-essential libc6-dev libfuse3-dev pkg-config fuse3 libudev-dev cargo build --release ``` diff --git a/vuinputd/Cargo.toml b/vuinputd/Cargo.toml index ae4690d..b252cd7 100644 --- a/vuinputd/Cargo.toml +++ b/vuinputd/Cargo.toml @@ -8,9 +8,9 @@ description = "Container-safe mediation daemon for /dev/uinput using CUSE." repository = "https://github.com/joleuger/vuinputd" [dependencies] -uinput-ioctls = { path = "../uinput-ioctls" } +uinput-ioctls = { path = "../uinput-ioctls", version = "0.1" } #fuse = "0.3" # FUSE/ CUSE interface -libfuse-sys = { git = "https://github.com/richard-w/libfuse-sys.git", rev = "a9bc85e3c24d44d8577e79759c4ccf0a18050037", features = ["fuse_35","cuse_lowlevel"] } +cuse-lowlevel = { path = "../cuse-lowlevel", version = "0.1" } #fuse-backend-rs = "0.13.0" nix = { version = "0.30", features = ["ioctl","process","sched","fs","event","user","socket","uio"] } libc = "0.2" # raw system calls diff --git a/vuinputd/src/main.rs b/vuinputd/src/main.rs index 53d8761..4be51ac 100644 --- a/vuinputd/src/main.rs +++ b/vuinputd/src/main.rs @@ -21,8 +21,7 @@ use libc::O_CLOEXEC; use libc::{iovec, off_t, size_t, EBADRQC, EIO, ENOENT}; use libc::{uinput_abs_setup, uinput_ff_erase, uinput_ff_upload, uinput_setup}; -use libfuse_sys::cuse_lowlevel; -use libfuse_sys::fuse_lowlevel; +use ::cuse_lowlevel::*; use log::{debug, error, info, trace}; use std::collections::HashMap; use std::ffi::{CStr, CString};