use crate::Result;
use bitflags::bitflags;
use errno::{errno, Errno};
use std::{
ffi::{CString, OsStr, OsString},
os::unix::{ffi::OsStrExt, io::RawFd},
path::Path,
ptr::null_mut,
};
bitflags! {
pub struct Flags: libc::c_int {
const XATTR_CREATE = libc::XATTR_CREATE;
const XATTR_REPLACE = libc::XATTR_REPLACE;
}
}
pub fn listxattr<P: AsRef<Path>>(path: P) -> Result<Vec<OsString>> {
let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
Ok(p) => p,
_ => return Err(Errno(libc::EINVAL)),
};
let buffer_size =
match unsafe { libc::listxattr(path.as_ptr(), null_mut(), 0) } {
-1 => return Err(errno()),
0 => return Ok(Vec::new()),
buffer_size => buffer_size as usize,
};
let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size);
let res = unsafe {
libc::listxattr(path.as_ptr(), buffer.as_mut_ptr().cast(), buffer_size)
};
match res {
-1 => Err(errno()),
len => {
unsafe { buffer.set_len(len as usize) };
Ok(buffer[..(len - 1) as usize]
.split(|&item| item == 0)
.map(OsStr::from_bytes)
.map(|str| str.to_owned())
.collect::<Vec<OsString>>())
}
}
}
pub fn llistxattr<P: AsRef<Path>>(path: P) -> Result<Vec<OsString>> {
let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
Ok(p) => p,
_ => return Err(Errno(libc::EINVAL)),
};
let buffer_size =
match unsafe { libc::llistxattr(path.as_ptr(), null_mut(), 0) } {
-1 => return Err(errno()),
0 => return Ok(Vec::new()),
buffer_size => buffer_size as usize,
};
let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size);
let res = unsafe {
libc::llistxattr(path.as_ptr(), buffer.as_mut_ptr().cast(), buffer_size)
};
match res {
-1 => Err(errno()),
len => {
unsafe { buffer.set_len(len as usize) };
Ok(buffer[..(len - 1) as usize]
.split(|&item| item == 0)
.map(OsStr::from_bytes)
.map(|str| str.to_owned())
.collect::<Vec<OsString>>())
}
}
}
pub fn flistxattr(fd: RawFd) -> Result<Vec<OsString>> {
let buffer_size = match unsafe { libc::flistxattr(fd, null_mut(), 0) } {
-1 => return Err(errno()),
0 => return Ok(Vec::new()),
buffer_size => buffer_size as usize,
};
let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size);
let res = unsafe {
libc::flistxattr(fd, buffer.as_mut_ptr().cast(), buffer_size)
};
match res {
-1 => Err(errno()),
len => {
unsafe { buffer.set_len(len as usize) };
Ok(buffer[..(len - 1) as usize]
.split(|&item| item == 0)
.map(OsStr::from_bytes)
.map(|str| str.to_owned())
.collect::<Vec<OsString>>())
}
}
}
pub fn getxattr<P, S>(path: P, name: S) -> Result<Vec<u8>>
where
P: AsRef<Path>,
S: AsRef<OsStr>,
{
let name = match CString::new(name.as_ref().as_bytes()) {
Ok(n) => n,
_ => return Err(Errno(libc::EINVAL)),
};
let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
Ok(n) => n,
_ => return Err(Errno(libc::EINVAL)),
};
let buffer_size = match unsafe {
libc::getxattr(path.as_ptr(), name.as_ptr(), null_mut(), 0)
} {
-1 => return Err(errno()),
0 => return Ok(Vec::new()),
buffer_size => buffer_size as usize,
};
let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size);
let res = unsafe {
libc::getxattr(
path.as_ptr(),
name.as_ptr(),
buffer.as_mut_ptr().cast(),
buffer_size,
)
};
match res {
-1 => Err(errno()),
len => {
unsafe { buffer.set_len(len as usize) };
Ok(buffer)
}
}
}
pub fn lgetxattr<P, S>(path: P, name: S) -> Result<Vec<u8>>
where
P: AsRef<Path>,
S: AsRef<OsStr>,
{
let name = match CString::new(name.as_ref().as_bytes()) {
Ok(n) => n,
_ => return Err(Errno(libc::EINVAL)),
};
let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
Ok(n) => n,
_ => return Err(Errno(libc::EINVAL)),
};
let buffer_size = match unsafe {
libc::lgetxattr(path.as_ptr(), name.as_ptr(), null_mut(), 0)
} {
-1 => return Err(errno()),
0 => return Ok(Vec::new()),
buffer_size => buffer_size as usize,
};
let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size);
let res = unsafe {
libc::lgetxattr(
path.as_ptr(),
name.as_ptr(),
buffer.as_mut_ptr().cast(),
buffer_size,
)
};
match res {
-1 => Err(errno()),
len => {
unsafe { buffer.set_len(len as usize) };
Ok(buffer)
}
}
}
pub fn fgetxattr<S>(fd: RawFd, name: S) -> Result<Vec<u8>>
where
S: AsRef<OsStr>,
{
let name = match CString::new(name.as_ref().as_bytes()) {
Ok(name) => name,
_ => return Err(Errno(libc::EINVAL)),
};
let buffer_size =
match unsafe { libc::fgetxattr(fd, name.as_ptr(), null_mut(), 0) } {
-1 => return Err(errno()),
0 => return Ok(Vec::new()),
buffer_size => buffer_size as usize,
};
let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size);
let res = unsafe {
libc::fgetxattr(
fd,
name.as_ptr(),
buffer.as_mut_ptr().cast(),
buffer_size,
)
};
match res {
-1 => Err(errno()),
len => {
unsafe { buffer.set_len(len as usize) };
Ok(buffer)
}
}
}
pub fn removexattr<P, S>(path: P, name: S) -> Result<()>
where
P: AsRef<Path>,
S: AsRef<OsStr>,
{
let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
Ok(n) => n,
_ => return Err(Errno(libc::EINVAL)),
};
let name = match CString::new(name.as_ref().as_bytes()) {
Ok(name) => name,
_ => return Err(Errno(libc::EINVAL)),
};
let res = unsafe { libc::removexattr(path.as_ptr(), name.as_ptr()) };
match res {
-1 => Err(errno()),
_ => Ok(()),
}
}
pub fn lremovexattr<P, S>(path: P, name: S) -> Result<()>
where
P: AsRef<Path>,
S: AsRef<OsStr>,
{
let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
Ok(n) => n,
_ => return Err(Errno(libc::EINVAL)),
};
let name = match CString::new(name.as_ref().as_bytes()) {
Ok(name) => name,
_ => return Err(Errno(libc::EINVAL)),
};
let res = unsafe { libc::lremovexattr(path.as_ptr(), name.as_ptr()) };
match res {
-1 => Err(errno()),
_ => Ok(()),
}
}
pub fn fremovexattr<S>(fd: RawFd, name: S) -> Result<()>
where
S: AsRef<OsStr>,
{
let name = match CString::new(name.as_ref().as_bytes()) {
Ok(name) => name,
_ => return Err(Errno(libc::EINVAL)),
};
let res = unsafe { libc::fremovexattr(fd, name.as_ptr()) };
match res {
-1 => Err(errno()),
_ => Ok(()),
}
}
pub fn setxattr<P, S, B>(path: P, name: S, value: B, flags: Flags) -> Result<()>
where
P: AsRef<Path>,
S: AsRef<OsStr>,
B: AsRef<[u8]>,
{
let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
Ok(n) => n,
_ => return Err(Errno(libc::EINVAL)),
};
let name = match CString::new(name.as_ref().as_bytes()) {
Ok(name) => name,
_ => return Err(Errno(libc::EINVAL)),
};
let value_ptr = value.as_ref().as_ptr().cast();
let value_len = value.as_ref().len();
let res = unsafe {
libc::setxattr(
path.as_ptr(),
name.as_ptr(),
value_ptr,
value_len,
flags.bits(),
)
};
match res {
-1 => Err(errno()),
_ => Ok(()),
}
}
pub fn lsetxattr<P, S, B>(
path: P,
name: S,
value: B,
flags: Flags,
) -> Result<()>
where
P: AsRef<Path>,
S: AsRef<OsStr>,
B: AsRef<[u8]>,
{
let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
Ok(n) => n,
_ => return Err(Errno(libc::EINVAL)),
};
let name = match CString::new(name.as_ref().as_bytes()) {
Ok(name) => name,
_ => return Err(Errno(libc::EINVAL)),
};
let value_ptr = value.as_ref().as_ptr().cast();
let value_len = value.as_ref().len();
let res = unsafe {
libc::lsetxattr(
path.as_ptr(),
name.as_ptr(),
value_ptr,
value_len,
flags.bits(),
)
};
match res {
-1 => Err(errno()),
_ => Ok(()),
}
}
pub fn fsetxattr<S, B>(fd: RawFd, name: S, value: B, flags: Flags) -> Result<()>
where
S: AsRef<OsStr>,
B: AsRef<[u8]>,
{
let name = match CString::new(name.as_ref().as_bytes()) {
Ok(name) => name,
_ => return Err(Errno(libc::EINVAL)),
};
let value_ptr = value.as_ref().as_ptr().cast();
let value_len = value.as_ref().len();
let res = unsafe {
libc::fsetxattr(fd, name.as_ptr(), value_ptr, value_len, flags.bits())
};
match res {
-1 => Err(errno()),
_ => Ok(()),
}
}