xattr/sys/
linux_macos.rs
1use std::ffi::{OsStr, OsString};
2use std::io;
3use std::mem;
4use std::os::unix::ffi::OsStrExt;
5use std::os::unix::io::BorrowedFd;
6use std::path::Path;
7
8use rustix::fs as rfs;
9use rustix::path::Arg;
10
11use crate::util::allocate_loop;
12
13use std::os::raw::c_char;
14
15#[cfg(not(target_os = "macos"))]
16pub const ENOATTR: i32 = rustix::io::Errno::NODATA.raw_os_error();
17
18#[cfg(target_os = "macos")]
19pub const ENOATTR: i32 = rustix::io::Errno::NOATTR.raw_os_error();
20
21#[inline]
23fn as_listxattr_buffer(buf: &mut [u8]) -> &mut [c_char] {
24 unsafe { &mut *(buf as *mut [u8] as *mut [c_char]) }
26}
27
28#[derive(Default)]
30pub struct XAttrs {
31 data: Box<[u8]>,
32 offset: usize,
33}
34
35impl 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 = mem::replace(&mut self.data, Box::new([])).into_vec();
46 data.extend(other.data.iter().cloned());
47 self.data = data.into_boxed_slice();
48 }
49}
50
51impl Iterator for XAttrs {
55 type Item = OsString;
56 fn next(&mut self) -> Option<OsString> {
57 let data = &self.data[self.offset..];
58 if data.is_empty() {
59 None
60 } else {
61 let end = data.iter().position(|&b| 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
77pub fn get_fd(fd: BorrowedFd<'_>, name: &OsStr) -> io::Result<Vec<u8>> {
78 allocate_loop(|buf| rfs::fgetxattr(fd, name, buf))
79}
80
81pub fn set_fd(fd: BorrowedFd<'_>, name: &OsStr, value: &[u8]) -> io::Result<()> {
82 rfs::fsetxattr(fd, name, value, rfs::XattrFlags::empty())?;
83 Ok(())
84}
85
86pub fn remove_fd(fd: BorrowedFd<'_>, name: &OsStr) -> io::Result<()> {
87 rfs::fremovexattr(fd, name)?;
88 Ok(())
89}
90
91pub fn list_fd(fd: BorrowedFd<'_>) -> io::Result<XAttrs> {
92 let vec = allocate_loop(|buf| rfs::flistxattr(fd, as_listxattr_buffer(buf)))?;
93 Ok(XAttrs {
94 data: vec.into_boxed_slice(),
95 offset: 0,
96 })
97}
98
99pub fn get_path(path: &Path, name: &OsStr, deref: bool) -> io::Result<Vec<u8>> {
100 let path = path.into_c_str()?;
101 let name = name.into_c_str()?;
102
103 allocate_loop(|buf| {
104 let getxattr_func = if deref { rfs::getxattr } else { rfs::lgetxattr };
105 let size = getxattr_func(&*path, &*name, buf)?;
106 io::Result::Ok(size)
107 })
108}
109
110pub fn set_path(path: &Path, name: &OsStr, value: &[u8], deref: bool) -> io::Result<()> {
111 let setxattr_func = if deref { rfs::setxattr } else { rfs::lsetxattr };
112 setxattr_func(path, name, value, rfs::XattrFlags::empty())?;
113 Ok(())
114}
115
116pub fn remove_path(path: &Path, name: &OsStr, deref: bool) -> io::Result<()> {
117 let removexattr_func = if deref {
118 rfs::removexattr
119 } else {
120 rfs::lremovexattr
121 };
122 removexattr_func(path, name)?;
123 Ok(())
124}
125
126pub fn list_path(path: &Path, deref: bool) -> io::Result<XAttrs> {
127 let listxattr_func = if deref {
128 rfs::listxattr
129 } else {
130 rfs::llistxattr
131 };
132 let path = path.as_cow_c_str()?;
133 let vec = allocate_loop(|buf| listxattr_func(&*path, as_listxattr_buffer(buf)))?;
134 Ok(XAttrs {
135 data: vec.into_boxed_slice(),
136 offset: 0,
137 })
138}