| 1 | use std::ffi::{OsStr, OsString}; | 
| 2 | use std::io; | 
|---|
| 3 | use std::mem; | 
|---|
| 4 | use std::os::unix::ffi::OsStrExt; | 
|---|
| 5 | use std::os::unix::io::BorrowedFd; | 
|---|
| 6 | use std::path::Path; | 
|---|
| 7 |  | 
|---|
| 8 | use rustix::fs as rfs; | 
|---|
| 9 | use rustix::path::Arg; | 
|---|
| 10 |  | 
|---|
| 11 | use crate::util::allocate_loop; | 
|---|
| 12 |  | 
|---|
| 13 | use std::os::raw::c_char; | 
|---|
| 14 |  | 
|---|
| 15 | #[ cfg(not(target_os = "macos"))] | 
|---|
| 16 | pub const ENOATTR: i32 = rustix::io::Errno::NODATA.raw_os_error(); | 
|---|
| 17 |  | 
|---|
| 18 | #[ cfg(target_os = "macos")] | 
|---|
| 19 | pub const ENOATTR: i32 = rustix::io::Errno::NOATTR.raw_os_error(); | 
|---|
| 20 |  | 
|---|
| 21 | // Convert an `&mut [u8]` to an `&mut [c_char]` | 
|---|
| 22 | #[ inline] | 
|---|
| 23 | fn as_listxattr_buffer(buf: &mut [u8]) -> &mut [c_char] { | 
|---|
| 24 | // SAFETY: u8 and i8 have the same size and alignment | 
|---|
| 25 | unsafe { &mut *(buf as *mut [u8] as *mut [c_char]) } | 
|---|
| 26 | } | 
|---|
| 27 |  | 
|---|
| 28 | /// An iterator over a set of extended attributes names. | 
|---|
| 29 | #[ derive(Default)] | 
|---|
| 30 | pub struct XAttrs { | 
|---|
| 31 | data: Box<[u8]>, | 
|---|
| 32 | offset: usize, | 
|---|
| 33 | } | 
|---|
| 34 |  | 
|---|
| 35 | impl Clone for XAttrs { | 
|---|
| 36 | fn clone(&self) -> Self { | 
|---|
| 37 | XAttrs { | 
|---|
| 38 | data: Vec::from(&*self.data).into_boxed_slice(), | 
|---|
| 39 | offset: self.offset, | 
|---|
| 40 | } | 
|---|
| 41 | } | 
|---|
| 42 | fn clone_from(&mut self, other: &XAttrs) { | 
|---|
| 43 | self.offset = other.offset; | 
|---|
| 44 |  | 
|---|
| 45 | let mut data: Vec = mem::replace(&mut self.data, src:Box::new([])).into_vec(); | 
|---|
| 46 | data.extend(iter:other.data.iter().cloned()); | 
|---|
| 47 | self.data = data.into_boxed_slice(); | 
|---|
| 48 | } | 
|---|
| 49 | } | 
|---|
| 50 |  | 
|---|
| 51 | // Yes, I could avoid these allocations on linux/macos. However, if we ever want to be freebsd | 
|---|
| 52 | // compatible, we need to be able to prepend the namespace to the extended attribute names. | 
|---|
| 53 | // Furthermore, borrowing makes the API messy. | 
|---|
| 54 | impl Iterator for XAttrs { | 
|---|
| 55 | type Item = OsString; | 
|---|
| 56 | fn next(&mut self) -> Option<OsString> { | 
|---|
| 57 | let data: &[u8] = &self.data[self.offset..]; | 
|---|
| 58 | if data.is_empty() { | 
|---|
| 59 | None | 
|---|
| 60 | } else { | 
|---|
| 61 | // always null terminated (unless empty). | 
|---|
| 62 | let end: usize = data.iter().position(|&b: u8| b == 0u8).unwrap(); | 
|---|
| 63 | self.offset += end + 1; | 
|---|
| 64 | Some(OsStr::from_bytes(&data[..end]).to_owned()) | 
|---|
| 65 | } | 
|---|
| 66 | } | 
|---|
| 67 |  | 
|---|
| 68 | fn size_hint(&self) -> (usize, Option<usize>) { | 
|---|
| 69 | if self.data.len() == self.offset { | 
|---|
| 70 | (0, Some(0)) | 
|---|
| 71 | } else { | 
|---|
| 72 | (1, None) | 
|---|
| 73 | } | 
|---|
| 74 | } | 
|---|
| 75 | } | 
|---|
| 76 |  | 
|---|
| 77 | pub fn get_fd(fd: BorrowedFd<'_>, name: &OsStr) -> io::Result<Vec<u8>> { | 
|---|
| 78 | allocate_loop(|buf: &mut [u8]| rfs::fgetxattr(fd, name, value:buf)) | 
|---|
| 79 | } | 
|---|
| 80 |  | 
|---|
| 81 | pub fn set_fd(fd: BorrowedFd<'_>, name: &OsStr, value: &[u8]) -> io::Result<()> { | 
|---|
| 82 | rfs::fsetxattr(fd, name, value, flags:rfs::XattrFlags::empty())?; | 
|---|
| 83 | Ok(()) | 
|---|
| 84 | } | 
|---|
| 85 |  | 
|---|
| 86 | pub fn remove_fd(fd: BorrowedFd<'_>, name: &OsStr) -> io::Result<()> { | 
|---|
| 87 | rfs::fremovexattr(fd, name)?; | 
|---|
| 88 | Ok(()) | 
|---|
| 89 | } | 
|---|
| 90 |  | 
|---|
| 91 | pub fn list_fd(fd: BorrowedFd<'_>) -> io::Result<XAttrs> { | 
|---|
| 92 | let vec: Vec = allocate_loop(|buf: &mut [u8]| rfs::flistxattr(fd, list:as_listxattr_buffer(buf)))?; | 
|---|
| 93 | Ok(XAttrs { | 
|---|
| 94 | data: vec.into_boxed_slice(), | 
|---|
| 95 | offset: 0, | 
|---|
| 96 | }) | 
|---|
| 97 | } | 
|---|
| 98 |  | 
|---|
| 99 | pub fn get_path(path: &Path, name: &OsStr, deref: bool) -> io::Result<Vec<u8>> { | 
|---|
| 100 | let path: Cow<'_, CStr> = path.into_c_str()?; | 
|---|
| 101 | let name: Cow<'_, CStr> = name.into_c_str()?; | 
|---|
| 102 |  | 
|---|
| 103 | allocate_loop(|buf: &mut [u8]| { | 
|---|
| 104 | let getxattr_func: fn(&CStr, &CStr, &mut [u8]) -> Result<…, …> = if deref { rfs::getxattr } else { rfs::lgetxattr }; | 
|---|
| 105 | let size: usize = getxattr_func(&*path, &*name, buf)?; | 
|---|
| 106 | io::Result::Ok(size) | 
|---|
| 107 | }) | 
|---|
| 108 | } | 
|---|
| 109 |  | 
|---|
| 110 | pub fn set_path(path: &Path, name: &OsStr, value: &[u8], deref: bool) -> io::Result<()> { | 
|---|
| 111 | let setxattr_func: fn(&Path, &OsStr, &[u8], XattrFlags) -> … = if deref { rfs::setxattr } else { rfs::lsetxattr }; | 
|---|
| 112 | setxattr_func(path, name, value, rfs::XattrFlags::empty())?; | 
|---|
| 113 | Ok(()) | 
|---|
| 114 | } | 
|---|
| 115 |  | 
|---|
| 116 | pub fn remove_path(path: &Path, name: &OsStr, deref: bool) -> io::Result<()> { | 
|---|
| 117 | let removexattr_func: fn(&Path, &OsStr) -> Result<…, …> = if deref { | 
|---|
| 118 | rfs::removexattr | 
|---|
| 119 | } else { | 
|---|
| 120 | rfs::lremovexattr | 
|---|
| 121 | }; | 
|---|
| 122 | removexattr_func(path, name)?; | 
|---|
| 123 | Ok(()) | 
|---|
| 124 | } | 
|---|
| 125 |  | 
|---|
| 126 | pub fn list_path(path: &Path, deref: bool) -> io::Result<XAttrs> { | 
|---|
| 127 | let listxattr_func: fn(&CStr, &mut [i8]) -> Result<…, …> = if deref { | 
|---|
| 128 | rfs::listxattr | 
|---|
| 129 | } else { | 
|---|
| 130 | rfs::llistxattr | 
|---|
| 131 | }; | 
|---|
| 132 | let path: Cow<'_, CStr> = path.as_cow_c_str()?; | 
|---|
| 133 | let vec: Vec = allocate_loop(|buf: &mut [u8]| listxattr_func(&*path, as_listxattr_buffer(buf)))?; | 
|---|
| 134 | Ok(XAttrs { | 
|---|
| 135 | data: vec.into_boxed_slice(), | 
|---|
| 136 | offset: 0, | 
|---|
| 137 | }) | 
|---|
| 138 | } | 
|---|
| 139 |  | 
|---|