xattr.rs 7.1 KB

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