1 | use std::ffi::{OsStr, OsString}; |
2 | use std::io; |
3 | use std::mem; |
4 | use std::os::unix::ffi::OsStrExt; |
5 | use std::os::unix::io::BorrowedFd; |
6 | use std::path::Path; |
7 | |
8 | use rustix::fs as rfs; |
9 | use rustix::path::Arg; |
10 | |
11 | use crate::util::allocate_loop; |
12 | |
13 | use std::os::raw::c_char; |
14 | |
15 | #[cfg (not(target_os = "macos" ))] |
16 | pub const ENOATTR: i32 = rustix::io::Errno::NODATA.raw_os_error(); |
17 | |
18 | #[cfg (target_os = "macos" )] |
19 | pub const ENOATTR: i32 = rustix::io::Errno::NOATTR.raw_os_error(); |
20 | |
21 | // Convert an `&mut [u8]` to an `&mut [c_char]` |
22 | #[inline ] |
23 | fn 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. |
31 | pub struct XAttrs { |
32 | data: Box<[u8]>, |
33 | offset: usize, |
34 | } |
35 | |
36 | impl 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. |
55 | impl 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 | |
78 | pub 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 | |
84 | pub 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 | |
89 | pub fn remove_fd(fd: BorrowedFd<'_>, name: &OsStr) -> io::Result<()> { |
90 | rfs::fremovexattr(fd, name)?; |
91 | Ok(()) |
92 | } |
93 | |
94 | pub 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 | |
104 | pub 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 | |
115 | pub 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 | |
121 | pub 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 | |
127 | pub 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 | |