Repository: bytecodealliance/rustix (language: rust)

Issue:
Painful [i8] in rustix::fs::listxattr()
`listxattr()` is defined:

```rust
pub fn listxattr<P>(path: P, list: &mut [c::c_char]) -> io::Result<usize>
where
    P: path::Arg,
```

which seems reasonable enough.  It's `c_char` under there, of course.  Unfortunately, at least on my platform (x86_64) this type is equivalent to `&mut [i8]` which is a bit more of an oddity.

The issue here is that there are an awful lot of APIs for converting between string types in Rust, and pretty much none of them consider `[i8]` as a string representation.

In particular: the `name` field of `rustix::fs::getxattr()` wants a `&CStr`.  I'm not sure of a good way to get from one to the other.

The core slice type has `split_inclusive()` which is a very nice way to iterate the returned values.  You get the string plus the nul terminator, which you can then pass to `CStr:from_bytes_with_nul`.... if it were a `[u8]` slice.

But since it's `[i8]` you end up having to do an unsafe transmute or an extremely explicit character-by-character conversion.

Of course, because of the embedded nuls, this API can't return `CString`.  But how about some variant of `[u8]` instead?


Discussion / hints:
Another wish item if we're going to break/add API here: `readlink()` in rustix has a very nice auto-allocating wrapper which automatically returns the correct-sized result.  It would be lovely to see something like that here.
`getxattr()` could also use the "automatically return correct-sized object" treatment as well.
Fixed in 0.38.41, thanks!
How was it fixed? I can't find any mention of it being fixed and for me it's still taking in `&mut [i8]`.
I think it wasn't actually fixed indeed.  It started working at some point for me, but I think something else must have changed because a colleague was still running into the problem.
See https://github.com/containers/composefs-rs/pull/50 and https://github.com/bytecodealliance/rustix/pull/1061#issuecomment-2518467534
#945 is relevant. It seems that the definition of `c_char` changes based on what dependencies you have and if they are turning the `"std"` feature of `linux-raw-sys` on or off.

Fix patch:
diff --git a/src/backend/libc/fs/syscalls.rs b/src/backend/libc/fs/syscalls.rs
index 952c96242..84abe6260 100644
--- a/src/backend/libc/fs/syscalls.rs
+++ b/src/backend/libc/fs/syscalls.rs
@@ -2441,17 +2441,21 @@ pub(crate) fn fsetxattr(
 }
 
 #[cfg(any(apple, linux_kernel, target_os = "hurd"))]
-pub(crate) fn listxattr(path: &CStr, list: &mut [ffi::c_char]) -> io::Result<usize> {
+pub(crate) fn listxattr(path: &CStr, list: &mut [u8]) -> io::Result<usize> {
     #[cfg(not(apple))]
     unsafe {
-        ret_usize(c::listxattr(path.as_ptr(), list.as_mut_ptr(), list.len()))
+        ret_usize(c::listxattr(
+            path.as_ptr(),
+            list.as_mut_ptr().cast::<ffi::c_char>(),
+            list.len(),
+        ))
     }
 
     #[cfg(apple)]
     unsafe {
         ret_usize(c::listxattr(
             path.as_ptr(),
-            list.as_mut_ptr(),
+            list.as_mut_ptr().cast::<ffi::c_char>(),
             list.len(),
             0,
         ))
@@ -2459,17 +2463,21 @@ pub(crate) fn listxattr(path: &CStr, list: &mut [ffi::c_char]) -> io::Result<usi
 }
 
 #[cfg(any(apple, linux_kernel, target_os = "hurd"))]
-pub(crate) fn llistxattr(path: &CStr, list: &mut [ffi::c_char]) -> io::Result<usize> {
+pub(crate) fn llistxattr(path: &CStr, list: &mut [u8]) -> io::Result<usize> {
     #[cfg(not(apple))]
     unsafe {
-        ret_usize(c::llistxattr(path.as_ptr(), list.as_mut_ptr(), list.len()))
+        ret_usize(c::llistxattr(
+            path.as_ptr(),
+            list.as_mut_ptr().cast::<ffi::c_char>(),
+            list.len(),
+        ))
     }
 
     #[cfg(apple)]
     unsafe {
         ret_usize(c::listxattr(
             path.as_ptr(),
-            list.as_mut_ptr(),
+            list.as_mut_ptr().cast::<ffi::c_char>(),
             list.len(),
             c::XATTR_NOFOLLOW,
         ))
@@ -2477,17 +2485,26 @@ pub(crate) fn llistxattr(path: &CStr, list: &mut [ffi::c_char]) -> io::Result<us
 }
 
 #[cfg(any(apple, linux_kernel, target_os = "hurd"))]
-pub(crate) fn flistxattr(fd: BorrowedFd<'_>, list: &mut [ffi::c_char]) -> io::Result<usize> {
+pub(crate) fn flistxattr(fd: BorrowedFd<'_>, list: &mut [u8]) -> io::Result<usize> {
     let fd = borrowed_fd(fd);
 
     #[cfg(not(apple))]
     unsafe {
-        ret_usize(c::flistxattr(fd, list.as_mut_ptr(), list.len()))
+        ret_usize(c::flistxattr(
+            fd,
+            list.as_mut_ptr().cast::<ffi::c_char>(),
+            list.len(),
+        ))
     }
 
     #[cfg(apple)]
     unsafe {
-        ret_usize(c::flistxattr(fd, list.as_mut_ptr(), list.len(), 0))
+        ret_usize(c::flistxattr(
+            fd,
+            list.as_mut_ptr().cast::<ffi::c_char>(),
+            list.len(),
+            0,
+        ))
     }
 }
 
diff --git a/src/backend/linux_raw/fs/syscalls.rs b/src/backend/linux_raw/fs/syscalls.rs
index 3b995a659..67a0a236e 100644
--- a/src/backend/linux_raw/fs/syscalls.rs
+++ b/src/backend/linux_raw/fs/syscalls.rs
@@ -31,7 +31,7 @@ use crate::backend::conv::{loff_t, loff_t_from_u64, ret_u64};
 ))]
 use crate::fd::AsFd;
 use crate::fd::{BorrowedFd, OwnedFd};
-use crate::ffi::{self, CStr};
+use crate::ffi::CStr;
 #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
 use crate::fs::CWD;
 use crate::fs::{
@@ -1628,19 +1628,19 @@ pub(crate) fn fsetxattr(
 }
 
 #[inline]
-pub(crate) fn listxattr(path: &CStr, list: &mut [ffi::c_char]) -> io::Result<usize> {
+pub(crate) fn listxattr(path: &CStr, list: &mut [u8]) -> io::Result<usize> {
     let (list_addr_mut, list_len) = slice_mut(list);
     unsafe { ret_usize(syscall!(__NR_listxattr, path, list_addr_mut, list_len)) }
 }
 
 #[inline]
-pub(crate) fn llistxattr(path: &CStr, list: &mut [ffi::c_char]) -> io::Result<usize> {
+pub(crate) fn llistxattr(path: &CStr, list: &mut [u8]) -> io::Result<usize> {
     let (list_addr_mut, list_len) = slice_mut(list);
     unsafe { ret_usize(syscall!(__NR_llistxattr, path, list_addr_mut, list_len)) }
 }
 
 #[inline]
-pub(crate) fn flistxattr(fd: BorrowedFd<'_>, list: &mut [ffi::c_char]) -> io::Result<usize> {
+pub(crate) fn flistxattr(fd: BorrowedFd<'_>, list: &mut [u8]) -> io::Result<usize> {
     let (list_addr_mut, list_len) = slice_mut(list);
     unsafe { ret_usize(syscall!(__NR_flistxattr, fd, list_addr_mut, list_len)) }
 }
diff --git a/src/fs/xattr.rs b/src/fs/xattr.rs
index 4a792c6e1..c6687d0f7 100644
--- a/src/fs/xattr.rs
+++ b/src/fs/xattr.rs
@@ -137,7 +137,7 @@ pub fn fsetxattr<Fd: AsFd, Name: path::Arg>(
 ///
 /// [Linux]: https://man7.org/linux/man-pages/man2/listxattr.2.html
 #[inline]
-pub fn listxattr<P: path::Arg>(path: P, list: &mut [ffi::c_char]) -> io::Result<usize> {
+pub fn listxattr<P: path::Arg>(path: P, list: &mut [u8]) -> io::Result<usize> {
     path.into_with_c_str(|path| backend::fs::syscalls::listxattr(path, list))
 }
 
@@ -149,7 +149,7 @@ pub fn listxattr<P: path::Arg>(path: P, list: &mut [ffi::c_char]) -> io::Result<
 ///
 /// [Linux]: https://man7.org/linux/man-pages/man2/llistxattr.2.html
 #[inline]
-pub fn llistxattr<P: path::Arg>(path: P, list: &mut [ffi::c_char]) -> io::Result<usize> {
+pub fn llistxattr<P: path::Arg>(path: P, list: &mut [u8]) -> io::Result<usize> {
     path.into_with_c_str(|path| backend::fs::syscalls::llistxattr(path, list))
 }
 
@@ -161,7 +161,7 @@ pub fn llistxattr<P: path::Arg>(path: P, list: &mut [ffi::c_char]) -> io::Result
 ///
 /// [Linux]: https://man7.org/linux/man-pages/man2/flistxattr.2.html
 #[inline]
-pub fn flistxattr<Fd: AsFd>(fd: Fd, list: &mut [ffi::c_char]) -> io::Result<usize> {
+pub fn flistxattr<Fd: AsFd>(fd: Fd, list: &mut [u8]) -> io::Result<usize> {
     backend::fs::syscalls::flistxattr(fd.as_fd(), list)
 }
 


Test patch: