From a2278d23acf3f3f9b554c302099e8b451e8a28ce Mon Sep 17 00:00:00 2001 From: alogani Date: Mon, 6 Jan 2025 07:38:24 +0100 Subject: [PATCH 01/11] Add bsd workflow --- .cirrus.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .cirrus.yml diff --git a/.cirrus.yml b/.cirrus.yml new file mode 100644 index 0000000..c6db48e --- /dev/null +++ b/.cirrus.yml @@ -0,0 +1,11 @@ +freebsd_instance: + image_family: freebsd-14-0 + +task: + name: FreeBSD + setup_script: + - pkg install -y pkgconf fusefs-libs fusefs-libs3 rust + build_script: + - cargo build --features parallel + test_script: + - cargo test --features parallel \ No newline at end of file From 995ed4e1af9aa958b3e03d42e06fc5300076bcb9 Mon Sep 17 00:00:00 2001 From: Alogani Date: Sun, 8 Feb 2026 18:33:36 +0100 Subject: [PATCH 02/11] small modifs --- Cargo.toml | 4 ++-- src/unix_fs/bsd_fs.rs | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d32e1d1..a0d16d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,13 +14,13 @@ serial = [] parallel = ["dep:threadpool"] async = ["dep:async-trait", "dep:tokio"] deadlock_detection = ["parallel", "dep:parking_lot"] - +libfuse = ["fuser/libfuse"] [dependencies] # Core dependencies +fuser = { version = "0.16.0" } log = "0.4" libc = "0.2" -fuser = "0.16" bitflags = "2.10" # Parallel dependencies diff --git a/src/unix_fs/bsd_fs.rs b/src/unix_fs/bsd_fs.rs index 83b87f5..56ed9bd 100644 --- a/src/unix_fs/bsd_fs.rs +++ b/src/unix_fs/bsd_fs.rs @@ -1,9 +1,10 @@ pub use super::bsd_like_fs::*; use std::os::fd::*; - +use std::path::Path; use std::ffi::c_void; +use crate::PosixError; -use libc::{self, c_char, c_int, size_t, ssize_t}; +use libc::{self, c_char, c_int, size_t, ssize_t, off_t}; use super::{StatFs, cstring_from_path}; @@ -15,7 +16,7 @@ pub(super) unsafe fn setxattr( path: *const c_char, name: *const c_char, value: *const c_void, - _size: size_t, + size: size_t, _flags: c_int, ) -> c_int { libc::extattr_set_file(path, libc::EXTATTR_NAMESPACE_USER, name, value, size) From 0488668f0dd9e7d2c3d66ac60a928a0f1be74c84 Mon Sep 17 00:00:00 2001 From: Alogani Date: Sun, 8 Feb 2026 18:43:45 +0100 Subject: [PATCH 03/11] small modifs --- src/unix_fs/bsd_fs.rs | 78 +++++++++++++++++++++++++++++++++----- src/unix_fs/bsd_like_fs.rs | 4 +- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/src/unix_fs/bsd_fs.rs b/src/unix_fs/bsd_fs.rs index 56ed9bd..617f06c 100644 --- a/src/unix_fs/bsd_fs.rs +++ b/src/unix_fs/bsd_fs.rs @@ -9,19 +9,77 @@ use libc::{self, c_char, c_int, size_t, ssize_t, off_t}; use super::{StatFs, cstring_from_path}; pub(super) unsafe fn fallocate(fd: c_int, _mode: c_int, offset: off_t, len: off_t) -> c_int { - libc::posix_fallocate(fd, offset, len) + unsafe { libc::posix_fallocate(fd, offset, len) } } +/// Warning: untested function pub(super) unsafe fn setxattr( path: *const c_char, name: *const c_char, value: *const c_void, - size: size_t, - _flags: c_int, + size: libc::size_t, + position: u32, + flags: c_int, ) -> c_int { - libc::extattr_set_file(path, libc::EXTATTR_NAMESPACE_USER, name, value, size) - .try_into() - .unwrap() + if position != 0 { + return -libc::EOPNOTSUPP; + } + + let path_cstr = CStr::from_ptr(path); + let name_cstr = CStr::from_ptr(name); + let name_str = match name_cstr.to_str() { + Ok(s) => s, + Err(_) => return -libc::EINVAL, + }; + + // Determine namespace and strip prefix + let (ns, attr_name) = if name_str.starts_with("user.") { + (libc::EXTATTR_NAMESPACE_USER, &name_str[5..]) + } else if name_str.starts_with("system.") { + (libc::EXTATTR_NAMESPACE_SYSTEM, &name_str[7..]) + } else if name_str.starts_with("trusted.") { + (libc::EXTATTR_NAMESPACE_SYSTEM, &name_str[8..]) + } else if name_str.starts_with("security.") { + (libc::EXTATTR_NAMESPACE_SYSTEM, &name_str[9..]) + } else { + (libc::EXTATTR_NAMESPACE_USER, name_str) + }; + + let attr_name_c = match std::ffi::CString::new(attr_name) { + Ok(c) => c, + Err(_) => return -libc::EINVAL, + }; + + // Check existence for flags + let exists = libc::extattr_get_file( + path, + ns, + attr_name_c.as_ptr(), + ptr::null_mut(), + 0, + ) >= 0; + + if (flags & libc::XATTR_CREATE) != 0 && exists { + return -libc::EEXIST; + } + if (flags & libc::XATTR_REPLACE) != 0 && !exists { + return -libc::ENODATA; // or -ENOATTR, depending on convention + } + + // Set the attribute + let ret = unsafe { libc::extattr_set_file( + path, + ns, + attr_name_c.as_ptr(), + value as *mut c_void, // cast for mutability if needed + size, + ) }; + + if ret >= 0 { + 0 // Success + } else { + -libc::errno + } } pub(super) unsafe fn getxattr( @@ -30,20 +88,20 @@ pub(super) unsafe fn getxattr( value: *mut c_void, size: size_t, ) -> ssize_t { - libc::extattr_get_file(path, libc::EXTATTR_NAMESPACE_USER, name, value, size) + unsafe { libc::extattr_get_file(path, libc::EXTATTR_NAMESPACE_USER, name, value, size) } } pub(super) unsafe fn listxattr(path: *const c_char, list: *mut c_char, size: size_t) -> ssize_t { - libc::extattr_list_file( + unsafe { libc::extattr_list_file( path, libc::EXTATTR_NAMESPACE_USER, list as *mut c_void, size, - ) + ) } } pub(super) unsafe fn removexattr(path: *const c_char, name: *const c_char) -> c_int { - libc::extattr_delete_file(path, libc::EXTATTR_NAMESPACE_USER, name) + unsafe { libc::extattr_delete_file(path, libc::EXTATTR_NAMESPACE_USER, name) } } /// Retrieves file system statistics for the specified path. diff --git a/src/unix_fs/bsd_like_fs.rs b/src/unix_fs/bsd_like_fs.rs index d459909..08403c1 100644 --- a/src/unix_fs/bsd_like_fs.rs +++ b/src/unix_fs/bsd_like_fs.rs @@ -19,11 +19,11 @@ pub(super) unsafe fn renameat2( newpath: *const c_char, _flags: c_uint, ) -> c_int { - libc::renameat(olddirfd, oldpath, newdirfd, newpath) + unsafe { libc::renameat(olddirfd, oldpath, newdirfd, newpath) } } pub(super) unsafe fn fdatasync(fd: c_int) -> c_int { - libc::fsync(fd) + unsafe { libc::fsync(fd) } } /// Copies a range of data from one file to another. From a3920cec6f8470ca7f95aab3e6314084989d7dd8 Mon Sep 17 00:00:00 2001 From: Alogani Date: Sun, 8 Feb 2026 18:48:56 +0100 Subject: [PATCH 04/11] small modifs --- src/unix_fs/bsd_fs.rs | 48 +++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/src/unix_fs/bsd_fs.rs b/src/unix_fs/bsd_fs.rs index 617f06c..900e1b7 100644 --- a/src/unix_fs/bsd_fs.rs +++ b/src/unix_fs/bsd_fs.rs @@ -1,7 +1,8 @@ pub use super::bsd_like_fs::*; -use std::os::fd::*; use std::path::Path; -use std::ffi::c_void; +use std::ffi::CStr; +use std::os::raw::{c_char, c_int, c_void}; +use std::ptr; use crate::PosixError; use libc::{self, c_char, c_int, size_t, ssize_t, off_t}; @@ -19,21 +20,21 @@ pub(super) unsafe fn setxattr( value: *const c_void, size: libc::size_t, position: u32, - flags: c_int, + _flags: c_int, // ignored on FreeBSD ) -> c_int { if position != 0 { return -libc::EOPNOTSUPP; } - let path_cstr = CStr::from_ptr(path); - let name_cstr = CStr::from_ptr(name); - let name_str = match name_cstr.to_str() { + // Convert C strings → Rust &str (safe failure → EINVAL) + let name_str = match CStr::from_ptr(name).to_str() { Ok(s) => s, Err(_) => return -libc::EINVAL, }; - // Determine namespace and strip prefix - let (ns, attr_name) = if name_str.starts_with("user.") { + // Map Linux-style prefixed names → FreeBSD namespace + // Most real-world FUSE code does this even on pure-BSD targets + let (ns, attr_name): (c_int, &str) = if name_str.starts_with("user.") { (libc::EXTATTR_NAMESPACE_USER, &name_str[5..]) } else if name_str.starts_with("system.") { (libc::EXTATTR_NAMESPACE_SYSTEM, &name_str[7..]) @@ -42,43 +43,28 @@ pub(super) unsafe fn setxattr( } else if name_str.starts_with("security.") { (libc::EXTATTR_NAMESPACE_SYSTEM, &name_str[9..]) } else { + // Default = user namespace, no prefix stripping (libc::EXTATTR_NAMESPACE_USER, name_str) }; - let attr_name_c = match std::ffi::CString::new(attr_name) { + let attr_name_c = match CString::new(attr_name) { Ok(c) => c, Err(_) => return -libc::EINVAL, }; - // Check existence for flags - let exists = libc::extattr_get_file( + let ret = libc::extattr_set_file( path, ns, attr_name_c.as_ptr(), - ptr::null_mut(), - 0, - ) >= 0; - - if (flags & libc::XATTR_CREATE) != 0 && exists { - return -libc::EEXIST; - } - if (flags & libc::XATTR_REPLACE) != 0 && !exists { - return -libc::ENODATA; // or -ENOATTR, depending on convention - } - - // Set the attribute - let ret = unsafe { libc::extattr_set_file( - path, - ns, - attr_name_c.as_ptr(), - value as *mut c_void, // cast for mutability if needed + value as *mut c_void, // API wants mutable pointer size, - ) }; + ); if ret >= 0 { - 0 // Success + // On success, returns number of bytes written (should == size) + 0 } else { - -libc::errno + -(*libc::errno()) } } From 9eff30594169d62fc8cc59d36a0027132ff205e8 Mon Sep 17 00:00:00 2001 From: Alogani Date: Sun, 8 Feb 2026 18:51:22 +0100 Subject: [PATCH 05/11] small modifs --- src/unix_fs/bsd_fs.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/unix_fs/bsd_fs.rs b/src/unix_fs/bsd_fs.rs index 900e1b7..d431bd2 100644 --- a/src/unix_fs/bsd_fs.rs +++ b/src/unix_fs/bsd_fs.rs @@ -1,8 +1,7 @@ pub use super::bsd_like_fs::*; use std::path::Path; -use std::ffi::CStr; +use std::ffi::{CStr, Cstring}; use std::os::raw::{c_char, c_int, c_void}; -use std::ptr; use crate::PosixError; use libc::{self, c_char, c_int, size_t, ssize_t, off_t}; @@ -32,8 +31,7 @@ pub(super) unsafe fn setxattr( Err(_) => return -libc::EINVAL, }; - // Map Linux-style prefixed names → FreeBSD namespace - // Most real-world FUSE code does this even on pure-BSD targets + // Map common prefixed names → FreeBSD namespace let (ns, attr_name): (c_int, &str) = if name_str.starts_with("user.") { (libc::EXTATTR_NAMESPACE_USER, &name_str[5..]) } else if name_str.starts_with("system.") { @@ -43,11 +41,10 @@ pub(super) unsafe fn setxattr( } else if name_str.starts_with("security.") { (libc::EXTATTR_NAMESPACE_SYSTEM, &name_str[9..]) } else { - // Default = user namespace, no prefix stripping (libc::EXTATTR_NAMESPACE_USER, name_str) }; - let attr_name_c = match CString::new(attr_name) { + let attr_name_c: CString = match CString::new(attr_name) { Ok(c) => c, Err(_) => return -libc::EINVAL, }; @@ -56,15 +53,14 @@ pub(super) unsafe fn setxattr( path, ns, attr_name_c.as_ptr(), - value as *mut c_void, // API wants mutable pointer + value as *mut c_void, // FreeBSD wants mutable pointer size, ); if ret >= 0 { - // On success, returns number of bytes written (should == size) 0 } else { - -(*libc::errno()) + -(*libc::__error()) // correct FreeBSD way to read errno } } From b99f118f282169602fea967b62cee4699bb55bf0 Mon Sep 17 00:00:00 2001 From: Alogani Date: Sun, 8 Feb 2026 18:51:41 +0100 Subject: [PATCH 06/11] small modifs --- src/unix_fs/bsd_fs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unix_fs/bsd_fs.rs b/src/unix_fs/bsd_fs.rs index d431bd2..161aba4 100644 --- a/src/unix_fs/bsd_fs.rs +++ b/src/unix_fs/bsd_fs.rs @@ -1,6 +1,6 @@ pub use super::bsd_like_fs::*; use std::path::Path; -use std::ffi::{CStr, Cstring}; +use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_int, c_void}; use crate::PosixError; From 509e151190c5b14bf7cdd6afd11d2f8bbc39c8d9 Mon Sep 17 00:00:00 2001 From: Alogani Date: Sun, 8 Feb 2026 18:54:16 +0100 Subject: [PATCH 07/11] small modifs --- src/unix_fs/bsd_fs.rs | 75 ++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/src/unix_fs/bsd_fs.rs b/src/unix_fs/bsd_fs.rs index 161aba4..5c00caf 100644 --- a/src/unix_fs/bsd_fs.rs +++ b/src/unix_fs/bsd_fs.rs @@ -1,10 +1,9 @@ pub use super::bsd_like_fs::*; use std::path::Path; use std::ffi::{CStr, CString}; -use std::os::raw::{c_char, c_int, c_void}; use crate::PosixError; -use libc::{self, c_char, c_int, size_t, ssize_t, off_t}; +use libc::{self, c_char, c_int, size_t, ssize_t, off_t, c_void}; use super::{StatFs, cstring_from_path}; @@ -21,46 +20,48 @@ pub(super) unsafe fn setxattr( position: u32, _flags: c_int, // ignored on FreeBSD ) -> c_int { - if position != 0 { - return -libc::EOPNOTSUPP; - } + unsafe { + if position != 0 { + return -libc::EOPNOTSUPP; + } - // Convert C strings → Rust &str (safe failure → EINVAL) - let name_str = match CStr::from_ptr(name).to_str() { - Ok(s) => s, - Err(_) => return -libc::EINVAL, - }; + // Convert C strings → Rust &str (safe failure → EINVAL) + let name_str = match CStr::from_ptr(name).to_str() { + Ok(s) => s, + Err(_) => return -libc::EINVAL, + }; - // Map common prefixed names → FreeBSD namespace - let (ns, attr_name): (c_int, &str) = if name_str.starts_with("user.") { - (libc::EXTATTR_NAMESPACE_USER, &name_str[5..]) - } else if name_str.starts_with("system.") { - (libc::EXTATTR_NAMESPACE_SYSTEM, &name_str[7..]) - } else if name_str.starts_with("trusted.") { - (libc::EXTATTR_NAMESPACE_SYSTEM, &name_str[8..]) - } else if name_str.starts_with("security.") { - (libc::EXTATTR_NAMESPACE_SYSTEM, &name_str[9..]) - } else { - (libc::EXTATTR_NAMESPACE_USER, name_str) - }; + // Map common prefixed names → FreeBSD namespace + let (ns, attr_name): (c_int, &str) = if name_str.starts_with("user.") { + (libc::EXTATTR_NAMESPACE_USER, &name_str[5..]) + } else if name_str.starts_with("system.") { + (libc::EXTATTR_NAMESPACE_SYSTEM, &name_str[7..]) + } else if name_str.starts_with("trusted.") { + (libc::EXTATTR_NAMESPACE_SYSTEM, &name_str[8..]) + } else if name_str.starts_with("security.") { + (libc::EXTATTR_NAMESPACE_SYSTEM, &name_str[9..]) + } else { + (libc::EXTATTR_NAMESPACE_USER, name_str) + }; - let attr_name_c: CString = match CString::new(attr_name) { - Ok(c) => c, - Err(_) => return -libc::EINVAL, - }; + let attr_name_c: CString = match CString::new(attr_name) { + Ok(c) => c, + Err(_) => return -libc::EINVAL, + }; - let ret = libc::extattr_set_file( - path, - ns, - attr_name_c.as_ptr(), - value as *mut c_void, // FreeBSD wants mutable pointer - size, - ); + let ret = libc::extattr_set_file( + path, + ns, + attr_name_c.as_ptr(), + value as *mut c_void, // FreeBSD wants mutable pointer + size, + ); - if ret >= 0 { - 0 - } else { - -(*libc::__error()) // correct FreeBSD way to read errno + if ret >= 0 { + 0 + } else { + -(*libc::__error()) // correct FreeBSD way to read errno + } } } From 5a66618640b69803e0a0ddd8cc894e9776da6fc2 Mon Sep 17 00:00:00 2001 From: Alogani Date: Sun, 8 Feb 2026 19:06:33 +0100 Subject: [PATCH 08/11] small modifs --- tests/integration_test.rs | 20 +++++++++++++++----- tests/offset_test.rs | 20 +++++++++++++++----- tests/test_recursion.rs | 21 ++++++++++++++++----- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 75c2455..3b9e0e2 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -97,9 +97,19 @@ fn test_mirror_fs_operations() { } eprintln!("Unmounting filesystem..."); - let _ = std::process::Command::new("fusermount") - .arg("-u") - .arg(&mntpoint) - .status(); - handle.join().unwrap(); + #[cfg(not(target_os = "freebsd"))] + { + let _ = std::process::Command::new("fusermount") + .arg("-u") + .arg(&mntpoint) + .status(); + handle.join().unwrap(); + } + #[cfg(target_os = "freebsd")] // TODO: explore why error: no such file or directory happens there + { + let _ = std::process::Command::new("umount") + .arg(&mntpoint) + .status(); + handle.join(); + } } diff --git a/tests/offset_test.rs b/tests/offset_test.rs index 4e17539..605bd5c 100644 --- a/tests/offset_test.rs +++ b/tests/offset_test.rs @@ -95,9 +95,19 @@ fn test_mirror_fs_file_offsets() { } eprintln!("Unmounting filesystem..."); - let _ = std::process::Command::new("fusermount") - .arg("-u") - .arg(&mntpoint) - .status(); - handle.join().unwrap(); + #[cfg(not(target_os = "freebsd"))] + { + let _ = std::process::Command::new("fusermount") + .arg("-u") + .arg(&mntpoint) + .status(); + handle.join().unwrap(); + } + #[cfg(target_os = "freebsd")] // TODO: explore why error: no such file or directory happens there + { + let _ = std::process::Command::new("umount") + .arg(&mntpoint) + .status(); + handle.join(); + } } diff --git a/tests/test_recursion.rs b/tests/test_recursion.rs index 7e01db1..7881694 100644 --- a/tests/test_recursion.rs +++ b/tests/test_recursion.rs @@ -75,9 +75,20 @@ fn test_mirror_fs_recursion() { println!("Test passed: No infinite recursion detected."); eprintln!("Unmounting filesystem..."); - let _ = std::process::Command::new("fusermount") - .arg("-u") - .arg(&mntpoint) - .status(); - handle.join().unwrap(); + #[cfg(not(target_os = "freebsd"))] + { + let _ = std::process::Command::new("fusermount") + .arg("-u") + .arg(&mntpoint) + .status(); + handle.join().unwrap(); + } + #[cfg(target_os = "freebsd")] // TODO: explore why error: no such file or directory happens there + { + let _ = std::process::Command::new("umount") + .arg(&mntpoint) + .status(); + handle.join(); + } + } From db8ccc50954f37230e276ee218828655d187ac92 Mon Sep 17 00:00:00 2001 From: Alogani Date: Sun, 8 Feb 2026 19:16:13 +0100 Subject: [PATCH 09/11] small modifs --- tests/integration_test.rs | 3 ++- tests/offset_test.rs | 3 ++- tests/test_recursion.rs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 3b9e0e2..ea864ae 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -110,6 +110,7 @@ fn test_mirror_fs_operations() { let _ = std::process::Command::new("umount") .arg(&mntpoint) .status(); - handle.join(); + let _ = handle.join(); } + drop(mntpoint); } diff --git a/tests/offset_test.rs b/tests/offset_test.rs index 605bd5c..1c05dc0 100644 --- a/tests/offset_test.rs +++ b/tests/offset_test.rs @@ -108,6 +108,7 @@ fn test_mirror_fs_file_offsets() { let _ = std::process::Command::new("umount") .arg(&mntpoint) .status(); - handle.join(); + let _ = handle.join(); } + drop(mntpoint); } diff --git a/tests/test_recursion.rs b/tests/test_recursion.rs index 7881694..62bc6e7 100644 --- a/tests/test_recursion.rs +++ b/tests/test_recursion.rs @@ -88,7 +88,8 @@ fn test_mirror_fs_recursion() { let _ = std::process::Command::new("umount") .arg(&mntpoint) .status(); - handle.join(); + let _ = handle.join(); } + drop(mntpoint); } From 9cf44e1cb560d4a33bfdd415254e18d3a07235e0 Mon Sep 17 00:00:00 2001 From: Alogani Date: Sun, 8 Feb 2026 19:17:27 +0100 Subject: [PATCH 10/11] small modifs --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index c6db48e..ab523c0 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,5 +1,5 @@ freebsd_instance: - image_family: freebsd-14-0 + image_family: freebsd-14-3 task: name: FreeBSD From 4e61a594143ac37c06df0595c6915b164866cb5b Mon Sep 17 00:00:00 2001 From: Alogani Date: Sun, 8 Feb 2026 19:23:08 +0100 Subject: [PATCH 11/11] small modifs --- .cirrus.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index ab523c0..2138ad1 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -6,6 +6,6 @@ task: setup_script: - pkg install -y pkgconf fusefs-libs fusefs-libs3 rust build_script: - - cargo build --features parallel + - cargo build --features "parallel libfuse" test_script: - - cargo test --features parallel \ No newline at end of file + - cargo test --features "parallel libfuse" \ No newline at end of file