use std::io::Result;
use std::{
ffi::OsStr,
os::unix::ffi::OsStrExt,
path::{Path, PathBuf},
};
use cap_tempfile::cap_std::fs::Dir;
use rustix::buffer::spare_capacity;
use crate::dirext::validate_relpath_no_uplinks;
fn proc_self_path(d: &Dir, path: &Path) -> Result<PathBuf> {
use rustix::path::DecInt;
use std::os::fd::{AsFd, AsRawFd};
let path = validate_relpath_no_uplinks(path)?;
let mut pathbuf = PathBuf::from("/proc/self/fd");
pathbuf.push(DecInt::new(d.as_fd().as_raw_fd()));
pathbuf.push(path);
Ok(pathbuf)
}
pub(crate) fn impl_getxattr(d: &Dir, path: &Path, key: &OsStr) -> Result<Option<Vec<u8>>> {
let path = &proc_self_path(d, path)?;
let mut buf = Vec::with_capacity(256);
loop {
match rustix::fs::lgetxattr(path, key, spare_capacity(&mut buf)) {
Ok(_) => {
return Ok(Some(buf));
}
Err(rustix::io::Errno::NODATA) => {
return Ok(None);
}
Err(rustix::io::Errno::RANGE) => {
buf.reserve(buf.capacity().saturating_mul(2));
}
Err(e) => {
return Err(e.into());
}
}
}
}
#[derive(Debug)]
#[cfg(any(target_os = "android", target_os = "linux"))]
pub struct XattrList {
buf: Vec<u8>,
}
#[cfg(any(target_os = "android", target_os = "linux"))]
impl XattrList {
pub fn iter(&self) -> impl Iterator<Item = &'_ std::ffi::OsStr> {
self.buf.split(|&v| v == 0).filter_map(|v| {
if v.is_empty() {
None
} else {
Some(OsStr::from_bytes(v))
}
})
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
pub(crate) fn impl_listxattrs(d: &Dir, path: &Path) -> Result<XattrList> {
let path = &proc_self_path(d, path)?;
let mut buf = Vec::with_capacity(512);
loop {
match rustix::fs::llistxattr(path, spare_capacity(&mut buf)) {
Ok(_) => {
return Ok(XattrList { buf });
}
Err(rustix::io::Errno::RANGE) => {
buf.reserve(buf.capacity().saturating_mul(2));
}
Err(e) => {
return Err(e.into());
}
}
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
pub(crate) fn impl_setxattr(d: &Dir, path: &Path, key: &OsStr, value: &[u8]) -> Result<()> {
let path = &proc_self_path(d, path)?;
rustix::fs::lsetxattr(path, key, value, rustix::fs::XattrFlags::empty())?;
Ok(())
}