xattr.rs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. //! Extended attribute support for Darwin and Linux systems.
  2. #![allow(trivial_casts)] // for ARM
  3. use std::cmp::Ordering;
  4. use std::io;
  5. use std::path::Path;
  6. pub const ENABLED: bool =
  7. cfg!(feature="git") &&
  8. cfg!(any(target_os = "macos", target_os = "linux"));
  9. pub trait FileAttributes {
  10. fn attributes(&self) -> io::Result<Vec<Attribute>>;
  11. fn symlink_attributes(&self) -> io::Result<Vec<Attribute>>;
  12. }
  13. #[cfg(any(target_os = "macos", target_os = "linux"))]
  14. impl FileAttributes for Path {
  15. fn attributes(&self) -> io::Result<Vec<Attribute>> {
  16. list_attrs(&lister::Lister::new(FollowSymlinks::Yes), self)
  17. }
  18. fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
  19. list_attrs(&lister::Lister::new(FollowSymlinks::No), self)
  20. }
  21. }
  22. #[cfg(not(any(target_os = "macos", target_os = "linux")))]
  23. impl FileAttributes for Path {
  24. fn attributes(&self) -> io::Result<Vec<Attribute>> {
  25. Ok(Vec::new())
  26. }
  27. fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
  28. Ok(Vec::new())
  29. }
  30. }
  31. /// Attributes which can be passed to `Attribute::list_with_flags`
  32. #[cfg(any(target_os = "macos", target_os = "linux"))]
  33. #[derive(Copy, Clone)]
  34. pub enum FollowSymlinks {
  35. Yes,
  36. No,
  37. }
  38. /// Extended attribute
  39. #[derive(Debug, Clone)]
  40. pub struct Attribute {
  41. pub name: String,
  42. pub size: usize,
  43. }
  44. #[cfg(any(target_os = "macos", target_os = "linux"))]
  45. pub fn list_attrs(lister: &lister::Lister, path: &Path) -> io::Result<Vec<Attribute>> {
  46. use std::ffi::CString;
  47. let c_path = match path.to_str().and_then(|s| CString::new(s).ok()) {
  48. Some(cstring) => cstring,
  49. None => {
  50. return Err(io::Error::new(io::ErrorKind::Other, "Error: path somehow contained a NUL?"));
  51. }
  52. };
  53. let bufsize = lister.listxattr_first(&c_path);
  54. match bufsize.cmp(&0) {
  55. Ordering::Less => return Err(io::Error::last_os_error()),
  56. Ordering::Equal => return Ok(Vec::new()),
  57. Ordering::Greater => {},
  58. }
  59. let mut buf = vec![0_u8; bufsize as usize];
  60. let err = lister.listxattr_second(&c_path, &mut buf, bufsize);
  61. match err.cmp(&0) {
  62. Ordering::Less => return Err(io::Error::last_os_error()),
  63. Ordering::Equal => return Ok(Vec::new()),
  64. Ordering::Greater => {},
  65. }
  66. let mut names = Vec::new();
  67. if err > 0 {
  68. // End indices of the attribute names
  69. // the buffer contains 0-terminated c-strings
  70. let idx = buf.iter().enumerate().filter_map(|(i, v)|
  71. if *v == 0 { Some(i) } else { None }
  72. );
  73. let mut start = 0;
  74. for end in idx {
  75. let c_end = end + 1; // end of the c-string (including 0)
  76. let size = lister.getxattr(&c_path, &buf[start..c_end]);
  77. if size > 0 {
  78. names.push(Attribute {
  79. name: lister.translate_attribute_name(&buf[start..end]),
  80. size: size as usize,
  81. });
  82. }
  83. start = c_end;
  84. }
  85. }
  86. Ok(names)
  87. }
  88. #[cfg(target_os = "macos")]
  89. mod lister {
  90. use super::FollowSymlinks;
  91. use libc::{c_int, size_t, ssize_t, c_char, c_void};
  92. use std::ffi::CString;
  93. use std::ptr;
  94. extern "C" {
  95. fn listxattr(
  96. path: *const c_char,
  97. namebuf: *mut c_char,
  98. size: size_t,
  99. options: c_int,
  100. ) -> ssize_t;
  101. fn getxattr(
  102. path: *const c_char,
  103. name: *const c_char,
  104. value: *mut c_void,
  105. size: size_t,
  106. position: u32,
  107. options: c_int,
  108. ) -> ssize_t;
  109. }
  110. pub struct Lister {
  111. c_flags: c_int,
  112. }
  113. impl Lister {
  114. pub fn new(do_follow: FollowSymlinks) -> Self {
  115. let c_flags: c_int = match do_follow {
  116. FollowSymlinks::Yes => 0x0001,
  117. FollowSymlinks::No => 0x0000,
  118. };
  119. Self { c_flags }
  120. }
  121. pub fn translate_attribute_name(&self, input: &[u8]) -> String {
  122. unsafe { std::str::from_utf8_unchecked(input).into() }
  123. }
  124. pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
  125. unsafe {
  126. listxattr(
  127. c_path.as_ptr(),
  128. ptr::null_mut(),
  129. 0,
  130. self.c_flags,
  131. )
  132. }
  133. }
  134. pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
  135. unsafe {
  136. listxattr(
  137. c_path.as_ptr(),
  138. buf.as_mut_ptr() as *mut c_char,
  139. bufsize as size_t,
  140. self.c_flags,
  141. )
  142. }
  143. }
  144. pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
  145. unsafe {
  146. getxattr(
  147. c_path.as_ptr(),
  148. buf.as_ptr() as *const c_char,
  149. ptr::null_mut(),
  150. 0,
  151. 0,
  152. self.c_flags,
  153. )
  154. }
  155. }
  156. }
  157. }
  158. #[cfg(target_os = "linux")]
  159. mod lister {
  160. use std::ffi::CString;
  161. use libc::{size_t, ssize_t, c_char, c_void};
  162. use super::FollowSymlinks;
  163. use std::ptr;
  164. extern "C" {
  165. fn listxattr(
  166. path: *const c_char,
  167. list: *mut c_char,
  168. size: size_t,
  169. ) -> ssize_t;
  170. fn llistxattr(
  171. path: *const c_char,
  172. list: *mut c_char,
  173. size: size_t,
  174. ) -> ssize_t;
  175. fn getxattr(
  176. path: *const c_char,
  177. name: *const c_char,
  178. value: *mut c_void,
  179. size: size_t,
  180. ) -> ssize_t;
  181. fn lgetxattr(
  182. path: *const c_char,
  183. name: *const c_char,
  184. value: *mut c_void,
  185. size: size_t,
  186. ) -> ssize_t;
  187. }
  188. pub struct Lister {
  189. follow_symlinks: FollowSymlinks,
  190. }
  191. impl Lister {
  192. pub fn new(follow_symlinks: FollowSymlinks) -> Lister {
  193. Lister { follow_symlinks }
  194. }
  195. pub fn translate_attribute_name(&self, input: &[u8]) -> String {
  196. String::from_utf8_lossy(input).into_owned()
  197. }
  198. pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
  199. let listxattr = match self.follow_symlinks {
  200. FollowSymlinks::Yes => listxattr,
  201. FollowSymlinks::No => llistxattr,
  202. };
  203. unsafe {
  204. listxattr(
  205. c_path.as_ptr() as *const _,
  206. ptr::null_mut(),
  207. 0,
  208. )
  209. }
  210. }
  211. pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
  212. let listxattr = match self.follow_symlinks {
  213. FollowSymlinks::Yes => listxattr,
  214. FollowSymlinks::No => llistxattr,
  215. };
  216. unsafe {
  217. listxattr(
  218. c_path.as_ptr() as *const _,
  219. buf.as_mut_ptr() as *mut c_char,
  220. bufsize as size_t,
  221. )
  222. }
  223. }
  224. pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
  225. let getxattr = match self.follow_symlinks {
  226. FollowSymlinks::Yes => getxattr,
  227. FollowSymlinks::No => lgetxattr,
  228. };
  229. unsafe {
  230. getxattr(
  231. c_path.as_ptr() as *const _,
  232. buf.as_ptr() as *const c_char,
  233. ptr::null_mut(),
  234. 0,
  235. )
  236. }
  237. }
  238. }
  239. }