1 #[cfg(target_os = "linux")]
2 mod linux;
3 
4 #[cfg(target_os = "linux")]
5 use self::linux::*;
6 
7 #[cfg(target_os = "macos")]
8 mod macos;
9 
10 #[cfg(target_os = "macos")]
11 use self::macos::*;
12 
13 use std::ffi::{OsStr, OsString};
14 use std::io;
15 use std::mem;
16 use std::os::unix::ffi::OsStrExt;
17 use std::os::unix::io::RawFd;
18 use std::path::Path;
19 
20 use libc::{c_char, c_void, size_t};
21 
22 use util::{allocate_loop, name_to_c, path_to_c};
23 
24 /// An iterator over a set of extended attributes names.
25 pub struct XAttrs {
26     data: Box<[u8]>,
27     offset: usize,
28 }
29 
30 impl Clone for XAttrs {
clone(&self) -> Self31     fn clone(&self) -> Self {
32         XAttrs {
33             data: Vec::from(&*self.data).into_boxed_slice(),
34             offset: self.offset,
35         }
36     }
clone_from(&mut self, other: &XAttrs)37     fn clone_from(&mut self, other: &XAttrs) {
38         self.offset = other.offset;
39 
40         let mut data = mem::replace(&mut self.data, Box::new([])).into_vec();
41         data.extend(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.
49 impl Iterator for XAttrs {
50     type Item = OsString;
next(&mut self) -> Option<OsString>51     fn next(&mut self) -> Option<OsString> {
52         let data = &self.data[self.offset..];
53         if data.is_empty() {
54             None
55         } else {
56             // always null terminated (unless empty).
57             let end = data.iter().position(|&b| b == 0u8).unwrap();
58             self.offset += end + 1;
59             Some(OsStr::from_bytes(&data[..end]).to_owned())
60         }
61     }
62 
size_hint(&self) -> (usize, Option<usize>)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 
get_fd(fd: RawFd, name: &OsStr) -> io::Result<Vec<u8>>72 pub fn get_fd(fd: RawFd, name: &OsStr) -> io::Result<Vec<u8>> {
73     let name = name_to_c(name)?;
74     unsafe {
75         allocate_loop(|buf, len| fgetxattr(fd, name.as_ptr(), buf as *mut c_void, len as size_t))
76     }
77 }
78 
set_fd(fd: RawFd, name: &OsStr, value: &[u8]) -> io::Result<()>79 pub fn set_fd(fd: RawFd, name: &OsStr, value: &[u8]) -> io::Result<()> {
80     let name = name_to_c(name)?;
81     let ret = unsafe {
82         fsetxattr(
83             fd,
84             name.as_ptr(),
85             value.as_ptr() as *const c_void,
86             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 
remove_fd(fd: RawFd, name: &OsStr) -> io::Result<()>96 pub fn remove_fd(fd: RawFd, name: &OsStr) -> io::Result<()> {
97     let name = name_to_c(name)?;
98     let ret = unsafe { fremovexattr(fd, name.as_ptr()) };
99     if ret != 0 {
100         Err(io::Error::last_os_error())
101     } else {
102         Ok(())
103     }
104 }
105 
list_fd(fd: RawFd) -> io::Result<XAttrs>106 pub fn list_fd(fd: RawFd) -> io::Result<XAttrs> {
107     let vec =
108         unsafe { allocate_loop(|buf, len| flistxattr(fd, buf as *mut c_char, len as size_t))? };
109     Ok(XAttrs {
110         data: vec.into_boxed_slice(),
111         offset: 0,
112     })
113 }
114 
get_path(path: &Path, name: &OsStr) -> io::Result<Vec<u8>>115 pub fn get_path(path: &Path, name: &OsStr) -> io::Result<Vec<u8>> {
116     let name = name_to_c(name)?;
117     let path = path_to_c(path)?;
118     unsafe {
119         allocate_loop(|buf, len| {
120             lgetxattr(
121                 path.as_ptr(),
122                 name.as_ptr(),
123                 buf as *mut c_void,
124                 len as size_t,
125             )
126         })
127     }
128 }
129 
set_path(path: &Path, name: &OsStr, value: &[u8]) -> io::Result<()>130 pub fn set_path(path: &Path, name: &OsStr, value: &[u8]) -> io::Result<()> {
131     let name = name_to_c(name)?;
132     let path = path_to_c(path)?;
133     let ret = unsafe {
134         lsetxattr(
135             path.as_ptr(),
136             name.as_ptr(),
137             value.as_ptr() as *const c_void,
138             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 
remove_path(path: &Path, name: &OsStr) -> io::Result<()>148 pub fn remove_path(path: &Path, name: &OsStr) -> io::Result<()> {
149     let name = name_to_c(name)?;
150     let path = path_to_c(path)?;
151     let ret = unsafe { lremovexattr(path.as_ptr(), name.as_ptr()) };
152     if ret != 0 {
153         Err(io::Error::last_os_error())
154     } else {
155         Ok(())
156     }
157 }
158 
list_path(path: &Path) -> io::Result<XAttrs>159 pub fn list_path(path: &Path) -> io::Result<XAttrs> {
160     let path = path_to_c(path)?;
161     let vec = unsafe {
162         allocate_loop(|buf, len| llistxattr(path.as_ptr(), buf as *mut c_char, len as size_t))?
163     };
164     Ok(XAttrs {
165         data: vec.into_boxed_slice(),
166         offset: 0,
167     })
168 }
169