1 //! FreeBSD and NetBSD xattr support.
2
3 use std::ffi::{CString, OsStr, OsString};
4 use std::io;
5 use std::mem;
6 use std::os::unix::ffi::{OsStrExt, OsStringExt};
7 use std::os::unix::io::RawFd;
8 use std::path::Path;
9
10 use libc::{c_char, c_int, c_void, size_t, ssize_t, EPERM};
11 use util::{allocate_loop, path_to_c};
12
13 const EXTATTR_NAMESPACE_USER_STRING: &'static str = "user";
14 const EXTATTR_NAMESPACE_SYSTEM_STRING: &'static str = "system";
15 const EXTATTR_NAMESPACE_NAMES: [&'static str; 3] = [
16 "empty",
17 EXTATTR_NAMESPACE_USER_STRING,
18 EXTATTR_NAMESPACE_SYSTEM_STRING,
19 ];
20 const EXTATTR_NAMESPACE_USER: c_int = 1;
21 const EXTATTR_NAMESPACE_SYSTEM: c_int = 2;
22
23 extern "C" {
extattr_list_fd( fd: c_int, attrnamespace: c_int, data: *mut c_void, nbytes: size_t, ) -> ssize_t24 pub fn extattr_list_fd(
25 fd: c_int,
26 attrnamespace: c_int,
27 data: *mut c_void,
28 nbytes: size_t,
29 ) -> ssize_t;
extattr_get_fd( fd: c_int, attrnamespace: c_int, attrname: *const c_char, data: *mut c_void, nbytes: size_t, ) -> ssize_t30 pub fn extattr_get_fd(
31 fd: c_int,
32 attrnamespace: c_int,
33 attrname: *const c_char,
34 data: *mut c_void,
35 nbytes: size_t,
36 ) -> ssize_t;
extattr_delete_fd(fd: c_int, attrname: c_int, attrname: *const c_char) -> c_int37 pub fn extattr_delete_fd(fd: c_int, attrname: c_int, attrname: *const c_char) -> c_int;
extattr_set_fd( fd: c_int, attrname: c_int, attrname: *const c_char, data: *const c_void, nbytes: size_t, ) -> ssize_t38 pub fn extattr_set_fd(
39 fd: c_int,
40 attrname: c_int,
41 attrname: *const c_char,
42 data: *const c_void,
43 nbytes: size_t,
44 ) -> ssize_t;
45
extattr_list_link( path: *const c_char, attrnamespace: c_int, data: *mut c_void, nbytes: size_t, ) -> ssize_t46 pub fn extattr_list_link(
47 path: *const c_char,
48 attrnamespace: c_int,
49 data: *mut c_void,
50 nbytes: size_t,
51 ) -> ssize_t;
extattr_get_link( path: *const c_char, attrnamespace: c_int, attrname: *const c_char, data: *mut c_void, nbytes: size_t, ) -> ssize_t52 pub fn extattr_get_link(
53 path: *const c_char,
54 attrnamespace: c_int,
55 attrname: *const c_char,
56 data: *mut c_void,
57 nbytes: size_t,
58 ) -> ssize_t;
extattr_delete_link( path: *const c_char, attrname: c_int, attrname: *const c_char, ) -> c_int59 pub fn extattr_delete_link(
60 path: *const c_char,
61 attrname: c_int,
62 attrname: *const c_char,
63 ) -> c_int;
extattr_set_link( path: *const c_char, attrname: c_int, attrname: *const c_char, data: *const c_void, nbytes: size_t, ) -> ssize_t64 pub fn extattr_set_link(
65 path: *const c_char,
66 attrname: c_int,
67 attrname: *const c_char,
68 data: *const c_void,
69 nbytes: size_t,
70 ) -> ssize_t;
71 }
72
73 /// An iterator over a set of extended attributes names.
74 pub struct XAttrs {
75 user_attrs: Box<[u8]>,
76 system_attrs: Box<[u8]>,
77 offset: usize,
78 }
79
80 impl Clone for XAttrs {
clone(&self) -> Self81 fn clone(&self) -> Self {
82 XAttrs {
83 user_attrs: Vec::from(&*self.user_attrs).into_boxed_slice(),
84 system_attrs: Vec::from(&*self.system_attrs).into_boxed_slice(),
85 offset: self.offset,
86 }
87 }
88
clone_from(&mut self, other: &XAttrs)89 fn clone_from(&mut self, other: &XAttrs) {
90 self.offset = other.offset;
91
92 let mut data = mem::replace(&mut self.user_attrs, Box::new([])).into_vec();
93 data.extend(other.user_attrs.iter().cloned());
94 self.user_attrs = data.into_boxed_slice();
95
96 data = mem::replace(&mut self.system_attrs, Box::new([])).into_vec();
97 data.extend(other.system_attrs.iter().cloned());
98 self.system_attrs = data.into_boxed_slice();
99 }
100 }
101
102 impl Iterator for XAttrs {
103 type Item = OsString;
next(&mut self) -> Option<OsString>104 fn next(&mut self) -> Option<OsString> {
105 if self.user_attrs.is_empty() && self.system_attrs.is_empty() {
106 return None;
107 }
108
109 if self.offset == self.user_attrs.len() + self.system_attrs.len() {
110 return None;
111 }
112
113 let data = if self.offset < self.system_attrs.len() {
114 &self.system_attrs[self.offset..]
115 } else {
116 &self.user_attrs[self.offset - self.system_attrs.len()..]
117 };
118
119 let siz = data[0] as usize;
120
121 self.offset += siz + 1;
122 if self.offset < self.system_attrs.len() {
123 Some(prefix_namespace(
124 OsStr::from_bytes(&data[1..siz + 1]),
125 EXTATTR_NAMESPACE_SYSTEM,
126 ))
127 } else {
128 Some(prefix_namespace(
129 OsStr::from_bytes(&data[1..siz + 1]),
130 EXTATTR_NAMESPACE_USER,
131 ))
132 }
133 }
134
size_hint(&self) -> (usize, Option<usize>)135 fn size_hint(&self) -> (usize, Option<usize>) {
136 if self.user_attrs.len() + self.system_attrs.len() == self.offset {
137 (0, Some(0))
138 } else {
139 (1, None)
140 }
141 }
142 }
143
name_to_ns(name: &OsStr) -> io::Result<(c_int, CString)>144 fn name_to_ns(name: &OsStr) -> io::Result<(c_int, CString)> {
145 let mut groups = name.as_bytes().splitn(2, |&b| b == b'.').take(2);
146 let nsname = match groups.next() {
147 Some(s) => s,
148 None => {
149 return Err(io::Error::new(
150 io::ErrorKind::InvalidInput,
151 "couldn't find namespace",
152 ))
153 }
154 };
155
156 let propname = match groups.next() {
157 Some(s) => s,
158 None => {
159 return Err(io::Error::new(
160 io::ErrorKind::InvalidInput,
161 "couldn't find attribute",
162 ))
163 }
164 };
165
166 let ns_int = match EXTATTR_NAMESPACE_NAMES
167 .iter()
168 .position(|&s| s.as_bytes() == nsname)
169 {
170 Some(i) => i,
171 None => {
172 return Err(io::Error::new(
173 io::ErrorKind::InvalidInput,
174 "no matching namespace",
175 ))
176 }
177 };
178
179 Ok((ns_int as c_int, CString::new(propname)?))
180 }
181
prefix_namespace(attr: &OsStr, ns: c_int) -> OsString182 fn prefix_namespace(attr: &OsStr, ns: c_int) -> OsString {
183 let nsname = EXTATTR_NAMESPACE_NAMES[ns as usize];
184 let mut v = Vec::with_capacity(nsname.as_bytes().len() + attr.as_bytes().len() + 1);
185 v.extend(nsname.as_bytes());
186 v.extend(".".as_bytes());
187 v.extend(attr.as_bytes());
188 OsString::from_vec(v)
189 }
190
get_fd(fd: RawFd, name: &OsStr) -> io::Result<Vec<u8>>191 pub fn get_fd(fd: RawFd, name: &OsStr) -> io::Result<Vec<u8>> {
192 let (ns, name) = name_to_ns(name)?;
193 unsafe {
194 allocate_loop(|buf, len| {
195 extattr_get_fd(fd, ns, name.as_ptr(), buf as *mut c_void, len as size_t)
196 })
197 }
198 }
199
set_fd(fd: RawFd, name: &OsStr, value: &[u8]) -> io::Result<()>200 pub fn set_fd(fd: RawFd, name: &OsStr, value: &[u8]) -> io::Result<()> {
201 let (ns, name) = name_to_ns(name)?;
202 let ret = unsafe {
203 extattr_set_fd(
204 fd,
205 ns,
206 name.as_ptr(),
207 value.as_ptr() as *const c_void,
208 value.len() as size_t,
209 )
210 };
211 if ret == -1 {
212 Err(io::Error::last_os_error())
213 } else {
214 Ok(())
215 }
216 }
217
remove_fd(fd: RawFd, name: &OsStr) -> io::Result<()>218 pub fn remove_fd(fd: RawFd, name: &OsStr) -> io::Result<()> {
219 let (ns, name) = name_to_ns(name)?;
220 let ret = unsafe { extattr_delete_fd(fd, ns, name.as_ptr()) };
221 if ret != 0 {
222 Err(io::Error::last_os_error())
223 } else {
224 Ok(())
225 }
226 }
227
list_fd(fd: RawFd) -> io::Result<XAttrs>228 pub fn list_fd(fd: RawFd) -> io::Result<XAttrs> {
229 let sysvec = unsafe {
230 let res = allocate_loop(|buf, len| {
231 extattr_list_fd(
232 fd,
233 EXTATTR_NAMESPACE_SYSTEM,
234 buf as *mut c_void,
235 len as size_t,
236 )
237 });
238 // On FreeBSD, system attributes require root privileges to view. However,
239 // to mimic the behavior of listxattr in linux and osx, we need to query
240 // them anyway and return empty results if we get EPERM
241 match res {
242 Ok(v) => v,
243 Err(err) => {
244 if err.raw_os_error() == Some(EPERM) {
245 Vec::new()
246 } else {
247 return Err(err);
248 }
249 }
250 }
251 };
252
253 let uservec = unsafe {
254 let res = allocate_loop(|buf, len| {
255 extattr_list_fd(
256 fd,
257 EXTATTR_NAMESPACE_USER,
258 buf as *mut c_void,
259 len as size_t,
260 )
261 });
262 match res {
263 Ok(v) => v,
264 Err(err) => return Err(err),
265 }
266 };
267
268 Ok(XAttrs {
269 system_attrs: sysvec.into_boxed_slice(),
270 user_attrs: uservec.into_boxed_slice(),
271 offset: 0,
272 })
273 }
274
get_path(path: &Path, name: &OsStr) -> io::Result<Vec<u8>>275 pub fn get_path(path: &Path, name: &OsStr) -> io::Result<Vec<u8>> {
276 let (ns, name) = name_to_ns(name)?;
277 let path = path_to_c(path)?;
278 unsafe {
279 allocate_loop(|buf, len| {
280 extattr_get_link(
281 path.as_ptr(),
282 ns,
283 name.as_ptr(),
284 buf as *mut c_void,
285 len as size_t,
286 )
287 })
288 }
289 }
290
set_path(path: &Path, name: &OsStr, value: &[u8]) -> io::Result<()>291 pub fn set_path(path: &Path, name: &OsStr, value: &[u8]) -> io::Result<()> {
292 let (ns, name) = name_to_ns(name)?;
293 let path = path_to_c(path)?;
294 let ret = unsafe {
295 extattr_set_link(
296 path.as_ptr(),
297 ns,
298 name.as_ptr(),
299 value.as_ptr() as *const c_void,
300 value.len() as size_t,
301 )
302 };
303 if ret == -1 {
304 Err(io::Error::last_os_error())
305 } else {
306 Ok(())
307 }
308 }
309
remove_path(path: &Path, name: &OsStr) -> io::Result<()>310 pub fn remove_path(path: &Path, name: &OsStr) -> io::Result<()> {
311 let (ns, name) = name_to_ns(name)?;
312 let path = path_to_c(path)?;
313 let ret = unsafe { extattr_delete_link(path.as_ptr(), ns, name.as_ptr()) };
314 if ret != 0 {
315 Err(io::Error::last_os_error())
316 } else {
317 Ok(())
318 }
319 }
320
list_path(path: &Path) -> io::Result<XAttrs>321 pub fn list_path(path: &Path) -> io::Result<XAttrs> {
322 let path = path_to_c(path)?;
323 let sysvec = unsafe {
324 let res = allocate_loop(|buf, len| {
325 extattr_list_link(
326 path.as_ptr(),
327 EXTATTR_NAMESPACE_SYSTEM,
328 buf as *mut c_void,
329 len as size_t,
330 )
331 });
332 // On FreeBSD, system attributes require root privileges to view. However,
333 // to mimic the behavior of listxattr in linux and osx, we need to query
334 // them anyway and return empty results if we get EPERM
335 match res {
336 Ok(v) => v,
337 Err(err) => {
338 if err.raw_os_error() == Some(EPERM) {
339 Vec::new()
340 } else {
341 return Err(err);
342 }
343 }
344 }
345 };
346
347 let uservec = unsafe {
348 let res = allocate_loop(|buf, len| {
349 extattr_list_link(
350 path.as_ptr(),
351 EXTATTR_NAMESPACE_USER,
352 buf as *mut c_void,
353 len as size_t,
354 )
355 });
356 match res {
357 Ok(v) => v,
358 Err(err) => return Err(err),
359 }
360 };
361
362 Ok(XAttrs {
363 system_attrs: sysvec.into_boxed_slice(),
364 user_attrs: uservec.into_boxed_slice(),
365 offset: 0,
366 })
367 }
368