| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- //! Extended attribute support for Darwin and Linux systems.
- #![allow(trivial_casts)] // for ARM
- extern crate libc;
- 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![])
- }
- fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
- Ok(vec![])
- }
- }
- /// 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 mut names = Vec::new();
- let bufsize = lister.listxattr_first(&c_path);
- if bufsize < 0 {
- return Err(io::Error::last_os_error());
- }
- else if bufsize > 0 {
- let mut buf = vec![0u8; bufsize as usize];
- let err = lister.listxattr_second(&c_path, &mut buf, bufsize);
- if err < 0 {
- return Err(io::Error::last_os_error());
- }
- if err > 0 {
- // End indicies of the attribute names
- // the buffer contains 0-terminates 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 std::ffi::CString;
- use libc::{c_int, size_t, ssize_t, c_char, c_void, uint32_t};
- use super::FollowSymlinks;
- 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: uint32_t,
- options: c_int
- ) -> ssize_t;
- }
- pub struct Lister {
- c_flags: c_int,
- }
- impl Lister {
- pub fn new(do_follow: FollowSymlinks) -> Lister {
- let c_flags: c_int = match do_follow {
- FollowSymlinks::Yes => 0x0001,
- FollowSymlinks::No => 0x0000,
- };
- Lister { c_flags }
- }
- pub fn translate_attribute_name(&self, input: &[u8]) -> String {
- use std::str::from_utf8_unchecked;
- unsafe {
- 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
- )
- }
- }
- }
- }
|