1use std::ffi::{OsStr, OsString};
2use std::io;
3use std::mem;
4use std::os::unix::ffi::OsStrExt;
5use std::os::unix::io::BorrowedFd;
6use std::path::Path;
7
8use rustix::fs as rfs;
9use rustix::path::Arg;
10
11use crate::util::allocate_loop;
12
13use std::os::raw::c_char;
14
15#[cfg(not(target_os = "macos"))]
16pub const ENOATTR: i32 = rustix::io::Errno::NODATA.raw_os_error();
17
18#[cfg(target_os = "macos")]
19pub const ENOATTR: i32 = rustix::io::Errno::NOATTR.raw_os_error();
20
21// Convert an `&mut [u8]` to an `&mut [c_char]`
22#[inline]
23fn as_listxattr_buffer(buf: &mut [u8]) -> &mut [c_char] {
24 // SAFETY: u8 and i8 have the same size and alignment
25 unsafe {
26 &mut *(buf as *mut [u8] as *mut [c_char])
27 }
28}
29
30/// An iterator over a set of extended attributes names.
31pub struct XAttrs {
32 data: Box<[u8]>,
33 offset: usize,
34}
35
36impl Clone for XAttrs {
37 fn clone(&self) -> Self {
38 XAttrs {
39 data: Vec::from(&*self.data).into_boxed_slice(),
40 offset: self.offset,
41 }
42 }
43 fn clone_from(&mut self, other: &XAttrs) {
44 self.offset = other.offset;
45
46 let mut data: Vec = mem::replace(&mut self.data, src:Box::new([])).into_vec();
47 data.extend(iter:other.data.iter().cloned());
48 self.data = data.into_boxed_slice();
49 }
50}
51
52// Yes, I could avoid these allocations on linux/macos. However, if we ever want to be freebsd
53// compatible, we need to be able to prepend the namespace to the extended attribute names.
54// Furthermore, borrowing makes the API messy.
55impl Iterator for XAttrs {
56 type Item = OsString;
57 fn next(&mut self) -> Option<OsString> {
58 let data: &[u8] = &self.data[self.offset..];
59 if data.is_empty() {
60 None
61 } else {
62 // always null terminated (unless empty).
63 let end: usize = data.iter().position(|&b: u8| b == 0u8).unwrap();
64 self.offset += end + 1;
65 Some(OsStr::from_bytes(&data[..end]).to_owned())
66 }
67 }
68
69 fn size_hint(&self) -> (usize, Option<usize>) {
70 if self.data.len() == self.offset {
71 (0, Some(0))
72 } else {
73 (1, None)
74 }
75 }
76}
77
78pub fn get_fd(fd: BorrowedFd<'_>, name: &OsStr) -> io::Result<Vec<u8>> {
79 allocate_loop(|buf: &mut [u8]| {
80 rfs::fgetxattr(fd, name, value:buf)
81 })
82}
83
84pub fn set_fd(fd: BorrowedFd<'_>, name: &OsStr, value: &[u8]) -> io::Result<()> {
85 rfs::fsetxattr(fd, name, value, flags:rfs::XattrFlags::empty())?;
86 Ok(())
87}
88
89pub fn remove_fd(fd: BorrowedFd<'_>, name: &OsStr) -> io::Result<()> {
90 rfs::fremovexattr(fd, name)?;
91 Ok(())
92}
93
94pub fn list_fd(fd: BorrowedFd<'_>) -> io::Result<XAttrs> {
95 let vec: Vec = allocate_loop(|buf: &mut [u8]| {
96 rfs::flistxattr(fd, list:as_listxattr_buffer(buf))
97 })?;
98 Ok(XAttrs {
99 data: vec.into_boxed_slice(),
100 offset: 0,
101 })
102}
103
104pub fn get_path(path: &Path, name: &OsStr, deref: bool) -> io::Result<Vec<u8>> {
105 let path: Cow<'_, CStr> = path.into_c_str()?;
106 let name: Cow<'_, CStr> = name.into_c_str()?;
107
108 allocate_loop(|buf: &mut [u8]| {
109 let getxattr_func: fn(&CStr, &CStr, &mut [u8]) -> … = if deref { rfs::getxattr } else { rfs::lgetxattr };
110 let size: usize = getxattr_func(&*path, &*name, buf)?;
111 io::Result::Ok(size)
112 })
113}
114
115pub fn set_path(path: &Path, name: &OsStr, value: &[u8], deref: bool) -> io::Result<()> {
116 let setxattr_func: fn(&Path, &OsStr, &[u8], …) -> … = if deref { rfs::setxattr } else { rfs::lsetxattr };
117 setxattr_func(path, name, value, rfs::XattrFlags::empty())?;
118 Ok(())
119}
120
121pub fn remove_path(path: &Path, name: &OsStr, deref: bool) -> io::Result<()> {
122 let removexattr_func: fn(&Path, &OsStr) -> Result<…, …> = if deref { rfs::removexattr } else { rfs::lremovexattr };
123 removexattr_func(path, name)?;
124 Ok(())
125}
126
127pub fn list_path(path: &Path, deref: bool) -> io::Result<XAttrs> {
128 let listxattr_func: fn(&CStr, &mut [i8]) -> Result<…, …> = if deref { rfs::listxattr } else { rfs::llistxattr };
129 let path: Cow<'_, CStr> = path.as_cow_c_str()?;
130 let vec: Vec = allocate_loop(|buf: &mut [u8]| {
131 listxattr_func(&*path, as_listxattr_buffer(buf))
132 })?;
133 Ok(XAttrs {
134 data: vec.into_boxed_slice(),
135 offset: 0,
136 })
137}
138