From 3fa3b70e58864a5232e843d1ded5d763306fccc4 Mon Sep 17 00:00:00 2001 From: Pepper Gray Date: Sun, 19 Oct 2025 00:08:51 +0200 Subject: [PATCH] passthrough.rs fails with cannot find type `statx` in crate `libc` musl 1.2.5 added support for the statx system call rust ships unknown-linux-musl with musl 1.2.3 milage on other libs may varay Enable portability by repling libc::statx with rustix::statx Fixes: #431 Signed-off-by: Pepper Gray --- Cargo.lock | 30 ++++++++ src/devices/Cargo.toml | 1 + .../src/virtio/fs/linux/passthrough.rs | 76 +++++++++---------- 3 files changed, 67 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d2af589f5..2b48e1a6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -441,6 +441,7 @@ dependencies = [ "pipewire", "polly", "rand", + "rustix", "rutabaga_gfx", "thiserror 2.0.12", "utils", @@ -516,6 +517,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "flate2" version = "1.1.1" @@ -970,6 +981,12 @@ dependencies = [ "vm-memory", ] +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + [[package]] name = "log" version = "0.4.27" @@ -1419,6 +1436,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "rustversion" version = "1.0.21" diff --git a/src/devices/Cargo.toml b/src/devices/Cargo.toml index cf69d3caa..e0303f896 100644 --- a/src/devices/Cargo.toml +++ b/src/devices/Cargo.toml @@ -37,6 +37,7 @@ utils = { path = "../utils" } polly = { path = "../polly" } rutabaga_gfx = { path = "../rutabaga_gfx", features = ["virgl_renderer", "virgl_renderer_next"], optional = true } imago = { version = "0.1.4", features = ["sync-wrappers", "vm-memory"] } +rustix = { version = "1.1.2", features = ["fs"] } [target.'cfg(target_os = "macos")'.dependencies] hvf = { path = "../hvf" } diff --git a/src/devices/src/virtio/fs/linux/passthrough.rs b/src/devices/src/virtio/fs/linux/passthrough.rs index 4ed6e4b48..70837df78 100644 --- a/src/devices/src/virtio/fs/linux/passthrough.rs +++ b/src/devices/src/virtio/fs/linux/passthrough.rs @@ -147,52 +147,48 @@ fn stat(f: &File) -> io::Result { } fn statx(f: &File) -> io::Result<(libc::stat64, u64)> { - let mut stx = MaybeUninit::::zeroed(); + use rustix::fd::BorrowedFd; + use rustix::fs::{statx, AtFlags, StatxFlags}; // Safe because this is a constant value and a valid C string. let pathname = unsafe { CStr::from_bytes_with_nul_unchecked(EMPTY_CSTR) }; + let dirfd = unsafe { BorrowedFd::borrow_raw(f.as_raw_fd()) }; // Safe because the kernel will only write data in `st` and we check the return // value. - let res = unsafe { - libc::statx( - f.as_raw_fd(), - pathname.as_ptr(), - libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, - libc::STATX_BASIC_STATS | libc::STATX_MNT_ID, - stx.as_mut_ptr(), - ) - }; - if res >= 0 { - // Safe because the kernel guarantees that the struct is now fully initialized. - let stx = unsafe { stx.assume_init() }; - - // Unfortunately, we cannot use an initializer to create the stat64 object, - // because it may contain padding and reserved fields (depending on the - // architecture), and it does not implement the Default trait. - // So we take a zeroed struct and set what we can. (Zero in all fields is - // wrong, but safe.) - let mut st = unsafe { MaybeUninit::::zeroed().assume_init() }; - - st.st_dev = libc::makedev(stx.stx_dev_major, stx.stx_dev_minor); - st.st_ino = stx.stx_ino; - st.st_mode = stx.stx_mode as _; - st.st_nlink = stx.stx_nlink as _; - st.st_uid = stx.stx_uid; - st.st_gid = stx.stx_gid; - st.st_rdev = libc::makedev(stx.stx_rdev_major, stx.stx_rdev_minor); - st.st_size = stx.stx_size as _; - st.st_blksize = stx.stx_blksize as _; - st.st_blocks = stx.stx_blocks as _; - st.st_atime = stx.stx_atime.tv_sec; - st.st_atime_nsec = stx.stx_atime.tv_nsec as _; - st.st_mtime = stx.stx_mtime.tv_sec; - st.st_mtime_nsec = stx.stx_mtime.tv_nsec as _; - st.st_ctime = stx.stx_ctime.tv_sec; - st.st_ctime_nsec = stx.stx_ctime.tv_nsec as _; - Ok((st, stx.stx_mnt_id)) - } else { - Err(io::Error::last_os_error()) + match statx( + dirfd, + pathname, + AtFlags::EMPTY_PATH | AtFlags::SYMLINK_NOFOLLOW, + StatxFlags::BASIC_STATS | StatxFlags::MNT_ID, + ) { + Ok(stx) => { + // Unfortunately, we cannot use an initializer to create the stat64 object, + // because it may contain padding and reserved fields (depending on the + // architecture), and it does not implement the Default trait. + // So we take a zeroed struct and set what we can. (Zero in all fields is + // wrong, but safe.) + let mut st = unsafe { MaybeUninit::::zeroed().assume_init() }; + + st.st_dev = libc::makedev(stx.stx_dev_major, stx.stx_dev_minor); + st.st_ino = stx.stx_ino; + st.st_mode = stx.stx_mode as _; + st.st_nlink = stx.stx_nlink as _; + st.st_uid = stx.stx_uid; + st.st_gid = stx.stx_gid; + st.st_rdev = libc::makedev(stx.stx_rdev_major, stx.stx_rdev_minor); + st.st_size = stx.stx_size as _; + st.st_blksize = stx.stx_blksize as _; + st.st_blocks = stx.stx_blocks as _; + st.st_atime = stx.stx_atime.tv_sec; + st.st_atime_nsec = stx.stx_atime.tv_nsec as _; + st.st_mtime = stx.stx_mtime.tv_sec; + st.st_mtime_nsec = stx.stx_mtime.tv_nsec as _; + st.st_ctime = stx.stx_ctime.tv_sec; + st.st_ctime_nsec = stx.stx_ctime.tv_nsec as _; + Ok((st, stx.stx_mnt_id)) + } + Err(e) => Err(e.into()), } }