| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- //! Extended attribute support for Darwin and Linux systems.
- #![allow(trivial_casts)] // for ARM
- use std::cmp::Ordering;
- use std::io;
- use std::path::Path;
- pub const ENABLED: bool =
- cfg!(feature="git") &&
- cfg!(any(target_os = "macos", target_os = "linux"));
- pub trait FileAttributes {
- fn attributes(&self) -> io::Result<Vec<Attribute>>;
- fn symlink_attributes(&self) -> io::Result<Vec<Attribute>>;
- }
- #[cfg(any(target_os = "macos", target_os = "linux"))]
- impl FileAttributes for Path {
- fn attributes(&self) -> io::Result<Vec<Attribute>> {
- list_attrs(&lister::Lister::new(FollowSymlinks::Yes), self)
- }
- fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
- list_attrs(&lister::Lister::new(FollowSymlinks::No), self)
- }
- }
- #[cfg(not(any(target_os = "macos", target_os = "linux")))]
- impl FileAttributes for Path {
- fn attributes(&self) -> io::Result<Vec<Attribute>> {
- Ok(Vec::new())
- }
- fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
- Ok(Vec::new())
- }
- }
- /// Attributes which can be passed to `Attribute::list_with_flags`
- #[cfg(any(target_os = "macos", target_os = "linux"))]
- #[derive(Copy, Clone)]
- pub enum FollowSymlinks {
- Yes,
- No,
- }
- /// Extended attribute
- #[derive(Debug, Clone)]
- pub struct Attribute {
- pub name: String,
- pub size: usize,
- }
- #[cfg(any(target_os = "macos", target_os = "linux"))]
- pub fn list_attrs(lister: &lister::Lister, path: &Path) -> io::Result<Vec<Attribute>> {
- use std::ffi::CString;
- let c_path = match path.to_str().and_then(|s| CString::new(s).ok()) {
- Some(cstring) => cstring,
- None => {
- return Err(io::Error::new(io::ErrorKind::Other, "Error: path somehow contained a NUL?"));
- }
- };
- let bufsize = lister.listxattr_first(&c_path);
- match bufsize.cmp(&0) {
- Ordering::Less => return Err(io::Error::last_os_error()),
- Ordering::Equal => return Ok(Vec::new()),
- Ordering::Greater => {},
- }
- let mut buf = vec![0_u8; bufsize as usize];
- let err = lister.listxattr_second(&c_path, &mut buf, bufsize);
- match err.cmp(&0) {
- Ordering::Less => return Err(io::Error::last_os_error()),
- Ordering::Equal => return Ok(Vec::new()),
- Ordering::Greater => {},
- }
- let mut names = Vec::new();
- if err > 0 {
- // End indices of the attribute names
- // the buffer contains 0-terminated c-strings
- let idx = buf.iter().enumerate().filter_map(|(i, v)|
- if *v == 0 { Some(i) } else { None }
- );
- let mut start = 0;
- for end in idx {
- let c_end = end + 1; // end of the c-string (including 0)
- let size = lister.getxattr(&c_path, &buf[start..c_end]);
- if size > 0 {
- names.push(Attribute {
- name: lister.translate_attribute_name(&buf[start..end]),
- size: size as usize,
- });
- }
- start = c_end;
- }
- }
- Ok(names)
- }
- #[cfg(target_os = "macos")]
- mod lister {
- use super::FollowSymlinks;
- use libc::{c_int, size_t, ssize_t, c_char, c_void};
- use std::ffi::CString;
- use std::ptr;
- extern "C" {
- fn listxattr(
- path: *const c_char,
- namebuf: *mut c_char,
- size: size_t,
- options: c_int,
- ) -> ssize_t;
- fn getxattr(
- path: *const c_char,
- name: *const c_char,
- value: *mut c_void,
- size: size_t,
- position: u32,
- options: c_int,
- ) -> ssize_t;
- }
- pub struct Lister {
- c_flags: c_int,
- }
- impl Lister {
- pub fn new(do_follow: FollowSymlinks) -> Self {
- let c_flags: c_int = match do_follow {
- FollowSymlinks::Yes => 0x0001,
- FollowSymlinks::No => 0x0000,
- };
- Self { c_flags }
- }
- pub fn translate_attribute_name(&self, input: &[u8]) -> String {
- unsafe { std::str::from_utf8_unchecked(input).into() }
- }
- pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
- unsafe {
- listxattr(
- c_path.as_ptr(),
- ptr::null_mut(),
- 0,
- self.c_flags,
- )
- }
- }
- pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
- unsafe {
- listxattr(
- c_path.as_ptr(),
- buf.as_mut_ptr() as *mut c_char,
- bufsize as size_t,
- self.c_flags,
- )
- }
- }
- pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
- unsafe {
- getxattr(
- c_path.as_ptr(),
- buf.as_ptr() as *const c_char,
- ptr::null_mut(),
- 0,
- 0,
- self.c_flags,
- )
- }
- }
- }
- }
- #[cfg(target_os = "linux")]
- mod lister {
- use std::ffi::CString;
- use libc::{size_t, ssize_t, c_char, c_void};
- use super::FollowSymlinks;
- use std::ptr;
- extern "C" {
- fn listxattr(
- path: *const c_char,
- list: *mut c_char,
- size: size_t,
- ) -> ssize_t;
- fn llistxattr(
- path: *const c_char,
- list: *mut c_char,
- size: size_t,
- ) -> ssize_t;
- fn getxattr(
- path: *const c_char,
- name: *const c_char,
- value: *mut c_void,
- size: size_t,
- ) -> ssize_t;
- fn lgetxattr(
- path: *const c_char,
- name: *const c_char,
- value: *mut c_void,
- size: size_t,
- ) -> ssize_t;
- }
- pub struct Lister {
- follow_symlinks: FollowSymlinks,
- }
- impl Lister {
- pub fn new(follow_symlinks: FollowSymlinks) -> Lister {
- Lister { follow_symlinks }
- }
- pub fn translate_attribute_name(&self, input: &[u8]) -> String {
- String::from_utf8_lossy(input).into_owned()
- }
- pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
- let listxattr = match self.follow_symlinks {
- FollowSymlinks::Yes => listxattr,
- FollowSymlinks::No => llistxattr,
- };
- unsafe {
- listxattr(
- c_path.as_ptr() as *const _,
- ptr::null_mut(),
- 0,
- )
- }
- }
- pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
- let listxattr = match self.follow_symlinks {
- FollowSymlinks::Yes => listxattr,
- FollowSymlinks::No => llistxattr,
- };
- unsafe {
- listxattr(
- c_path.as_ptr() as *const _,
- buf.as_mut_ptr() as *mut c_char,
- bufsize as size_t,
- )
- }
- }
- pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
- let getxattr = match self.follow_symlinks {
- FollowSymlinks::Yes => getxattr,
- FollowSymlinks::No => lgetxattr,
- };
- unsafe {
- getxattr(
- c_path.as_ptr() as *const _,
- buf.as_ptr() as *const c_char,
- ptr::null_mut(),
- 0,
- )
- }
- }
- }
- }
|