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