1#[cfg(any(target_os = "linux", target_os = "android"))]
2mod linux;
3
4#[cfg(any(target_os = "linux", target_os = "android"))]
5use self::linux::*;
6
7#[cfg(target_os = "macos")]
8mod macos;
9
10#[cfg(target_os = "macos")]
11use self::macos::*;
12
13use std::ffi::{OsStr, OsString};
14use std::io;
15use std::mem;
16use std::os::unix::ffi::OsStrExt;
17use std::os::unix::io::RawFd;
18use std::path::Path;
19
20use libc::{c_char, c_void, size_t};
21
22use util::{allocate_loop, name_to_c, path_to_c};
23
24/// An iterator over a set of extended attributes names.
25pub struct XAttrs {
26 data: Box<[u8]>,
27 offset: usize,
28}
29
30impl Clone for XAttrs {
31 fn clone(&self) -> Self {
32 XAttrs {
33 data: Vec::from(&*self.data).into_boxed_slice(),
34 offset: self.offset,
35 }
36 }
37 fn clone_from(&mut self, other: &XAttrs) {
38 self.offset = other.offset;
39
40 let mut data: Vec = mem::replace(&mut self.data, src:Box::new([])).into_vec();
41 data.extend(iter:other.data.iter().cloned());
42 self.data = data.into_boxed_slice();
43 }
44}
45
46// Yes, I could avoid these allocations on linux/macos. However, if we ever want to be freebsd
47// compatible, we need to be able to prepend the namespace to the extended attribute names.
48// Furthermore, borrowing makes the API messy.
49impl Iterator for XAttrs {
50 type Item = OsString;
51 fn next(&mut self) -> Option<OsString> {
52 let data: &[u8] = &self.data[self.offset..];
53 if data.is_empty() {
54 None
55 } else {
56 // always null terminated (unless empty).
57 let end: usize = data.iter().position(|&b: u8| b == 0u8).unwrap();
58 self.offset += end + 1;
59 Some(OsStr::from_bytes(&data[..end]).to_owned())
60 }
61 }
62
63 fn size_hint(&self) -> (usize, Option<usize>) {
64 if self.data.len() == self.offset {
65 (0, Some(0))
66 } else {
67 (1, None)
68 }
69 }
70}
71
72pub fn get_fd(fd: RawFd, name: &OsStr) -> io::Result<Vec<u8>> {
73 let name: CString = name_to_c(name)?;
74 unsafe {
75 allocate_loop(|buf: *mut u8, len: usize| fgetxattr(fd, name:name.as_ptr(), value:buf as *mut c_void, size:len as size_t))
76 }
77}
78
79pub fn set_fd(fd: RawFd, name: &OsStr, value: &[u8]) -> io::Result<()> {
80 let name: CString = name_to_c(name)?;
81 let ret: isize = unsafe {
82 fsetxattr(
83 fd,
84 name:name.as_ptr(),
85 value:value.as_ptr() as *const c_void,
86 size:value.len() as size_t,
87 )
88 };
89 if ret != 0 {
90 Err(io::Error::last_os_error())
91 } else {
92 Ok(())
93 }
94}
95
96pub fn remove_fd(fd: RawFd, name: &OsStr) -> io::Result<()> {
97 let name: CString = name_to_c(name)?;
98 let ret: i32 = unsafe { fremovexattr(fd, name:name.as_ptr()) };
99 if ret != 0 {
100 Err(io::Error::last_os_error())
101 } else {
102 Ok(())
103 }
104}
105
106pub fn list_fd(fd: RawFd) -> io::Result<XAttrs> {
107 let vec: Vec =
108 unsafe { allocate_loop(|buf: *mut u8, len: usize| flistxattr(fd, buf as *mut c_char, size:len as size_t))? };
109 Ok(XAttrs {
110 data: vec.into_boxed_slice(),
111 offset: 0,
112 })
113}
114
115pub fn get_path(path: &Path, name: &OsStr) -> io::Result<Vec<u8>> {
116 let name: CString = name_to_c(name)?;
117 let path: CString = path_to_c(path)?;
118 unsafe {
119 allocate_loop(|buf: *mut u8, len: usize| {
120 lgetxattr(
121 path:path.as_ptr(),
122 name:name.as_ptr(),
123 value:buf as *mut c_void,
124 size:len as size_t,
125 )
126 })
127 }
128}
129
130pub fn set_path(path: &Path, name: &OsStr, value: &[u8]) -> io::Result<()> {
131 let name: CString = name_to_c(name)?;
132 let path: CString = path_to_c(path)?;
133 let ret: isize = unsafe {
134 lsetxattr(
135 path:path.as_ptr(),
136 name:name.as_ptr(),
137 value:value.as_ptr() as *const c_void,
138 size:value.len() as size_t,
139 )
140 };
141 if ret != 0 {
142 Err(io::Error::last_os_error())
143 } else {
144 Ok(())
145 }
146}
147
148pub fn remove_path(path: &Path, name: &OsStr) -> io::Result<()> {
149 let name: CString = name_to_c(name)?;
150 let path: CString = path_to_c(path)?;
151 let ret: i32 = unsafe { lremovexattr(path:path.as_ptr(), name:name.as_ptr()) };
152 if ret != 0 {
153 Err(io::Error::last_os_error())
154 } else {
155 Ok(())
156 }
157}
158
159pub fn list_path(path: &Path) -> io::Result<XAttrs> {
160 let path: CString = path_to_c(path)?;
161 let vec: Vec = unsafe {
162 allocate_loop(|buf: *mut u8, len: usize| llistxattr(path:path.as_ptr(), buf as *mut c_char, size:len as size_t))?
163 };
164 Ok(XAttrs {
165 data: vec.into_boxed_slice(),
166 offset: 0,
167 })
168}
169