#[cfg(any(target_os = "linux", target_os = "android"))] mod linux; #[cfg(any(target_os = "linux", target_os = "android"))] use self::linux::*; #[cfg(target_os = "macos")] mod macos; #[cfg(target_os = "macos")] use self::macos::*; use std::ffi::{OsStr, OsString}; use std::io; use std::mem; use std::os::unix::ffi::OsStrExt; use std::os::unix::io::RawFd; use std::path::Path; use libc::{c_char, c_void, size_t}; use util::{allocate_loop, name_to_c, path_to_c}; /// An iterator over a set of extended attributes names. pub struct XAttrs { data: Box<[u8]>, offset: usize, } impl Clone for XAttrs { fn clone(&self) -> Self { XAttrs { data: Vec::from(&*self.data).into_boxed_slice(), offset: self.offset, } } fn clone_from(&mut self, other: &XAttrs) { self.offset = other.offset; let mut data = mem::replace(&mut self.data, Box::new([])).into_vec(); data.extend(other.data.iter().cloned()); self.data = data.into_boxed_slice(); } } // Yes, I could avoid these allocations on linux/macos. However, if we ever want to be freebsd // compatible, we need to be able to prepend the namespace to the extended attribute names. // Furthermore, borrowing makes the API messy. impl Iterator for XAttrs { type Item = OsString; fn next(&mut self) -> Option { let data = &self.data[self.offset..]; if data.is_empty() { None } else { // always null terminated (unless empty). let end = data.iter().position(|&b| b == 0u8).unwrap(); self.offset += end + 1; Some(OsStr::from_bytes(&data[..end]).to_owned()) } } fn size_hint(&self) -> (usize, Option) { if self.data.len() == self.offset { (0, Some(0)) } else { (1, None) } } } pub fn get_fd(fd: RawFd, name: &OsStr) -> io::Result> { let name = name_to_c(name)?; unsafe { allocate_loop(|buf, len| fgetxattr(fd, name.as_ptr(), buf as *mut c_void, len as size_t)) } } pub fn set_fd(fd: RawFd, name: &OsStr, value: &[u8]) -> io::Result<()> { let name = name_to_c(name)?; let ret = unsafe { fsetxattr( fd, name.as_ptr(), value.as_ptr() as *const c_void, value.len() as size_t, ) }; if ret != 0 { Err(io::Error::last_os_error()) } else { Ok(()) } } pub fn remove_fd(fd: RawFd, name: &OsStr) -> io::Result<()> { let name = name_to_c(name)?; let ret = unsafe { fremovexattr(fd, name.as_ptr()) }; if ret != 0 { Err(io::Error::last_os_error()) } else { Ok(()) } } pub fn list_fd(fd: RawFd) -> io::Result { let vec = unsafe { allocate_loop(|buf, len| flistxattr(fd, buf as *mut c_char, len as size_t))? }; Ok(XAttrs { data: vec.into_boxed_slice(), offset: 0, }) } pub fn get_path(path: &Path, name: &OsStr) -> io::Result> { let name = name_to_c(name)?; let path = path_to_c(path)?; unsafe { allocate_loop(|buf, len| { lgetxattr( path.as_ptr(), name.as_ptr(), buf as *mut c_void, len as size_t, ) }) } } pub fn set_path(path: &Path, name: &OsStr, value: &[u8]) -> io::Result<()> { let name = name_to_c(name)?; let path = path_to_c(path)?; let ret = unsafe { lsetxattr( path.as_ptr(), name.as_ptr(), value.as_ptr() as *const c_void, value.len() as size_t, ) }; if ret != 0 { Err(io::Error::last_os_error()) } else { Ok(()) } } pub fn remove_path(path: &Path, name: &OsStr) -> io::Result<()> { let name = name_to_c(name)?; let path = path_to_c(path)?; let ret = unsafe { lremovexattr(path.as_ptr(), name.as_ptr()) }; if ret != 0 { Err(io::Error::last_os_error()) } else { Ok(()) } } pub fn list_path(path: &Path) -> io::Result { let path = path_to_c(path)?; let vec = unsafe { allocate_loop(|buf, len| llistxattr(path.as_ptr(), buf as *mut c_char, len as size_t))? }; Ok(XAttrs { data: vec.into_boxed_slice(), offset: 0, }) }