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;
9
10use crate::util::allocate_loop;
11
12#[cfg(not(target_os = "macos"))]
13pub const ENOATTR: i32 = rustix::io::Errno::NODATA.raw_os_error();
14
15#[cfg(target_os = "macos")]
16pub const ENOATTR: i32 = rustix::io::Errno::NOATTR.raw_os_error();
17
18pub const ERANGE: i32 = rustix::io::Errno::RANGE.raw_os_error();
19
20/// An iterator over a set of extended attributes names.
21#[derive(Default)]
22pub struct XAttrs {
23    data: Box<[u8]>,
24    offset: usize,
25}
26
27impl 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 = mem::replace(&mut self.data, Box::new([])).into_vec();
38        data.extend(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.
46impl Iterator for XAttrs {
47    type Item = OsString;
48    fn next(&mut self) -> Option<OsString> {
49        let data = &self.data[self.offset..];
50        if data.is_empty() {
51            None
52        } else {
53            // always null terminated (unless empty).
54            let end = data.iter().position(|&b| 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
69pub fn get_fd(fd: BorrowedFd<'_>, name: &OsStr) -> io::Result<Vec<u8>> {
70    allocate_loop(|buf| rfs::fgetxattr(fd, name, buf))
71}
72
73pub fn set_fd(fd: BorrowedFd<'_>, name: &OsStr, value: &[u8]) -> io::Result<()> {
74    rfs::fsetxattr(fd, name, value, rfs::XattrFlags::empty())?;
75    Ok(())
76}
77
78pub fn remove_fd(fd: BorrowedFd<'_>, name: &OsStr) -> io::Result<()> {
79    rfs::fremovexattr(fd, name)?;
80    Ok(())
81}
82
83pub fn list_fd(fd: BorrowedFd<'_>) -> io::Result<XAttrs> {
84    let vec = allocate_loop(|buf| rfs::flistxattr(fd, buf))?;
85    Ok(XAttrs {
86        data: vec.into_boxed_slice(),
87        offset: 0,
88    })
89}
90
91pub fn get_path(path: &Path, name: &OsStr, deref: bool) -> io::Result<Vec<u8>> {
92    allocate_loop(|buf| {
93        let getxattr_func = if deref { rfs::getxattr } else { rfs::lgetxattr };
94        let size = getxattr_func(path, name, buf)?;
95        io::Result::Ok(size)
96    })
97}
98
99pub fn set_path(path: &Path, name: &OsStr, value: &[u8], deref: bool) -> io::Result<()> {
100    let setxattr_func = if deref { rfs::setxattr } else { rfs::lsetxattr };
101    setxattr_func(path, name, value, rfs::XattrFlags::empty())?;
102    Ok(())
103}
104
105pub fn remove_path(path: &Path, name: &OsStr, deref: bool) -> io::Result<()> {
106    let removexattr_func = if deref {
107        rfs::removexattr
108    } else {
109        rfs::lremovexattr
110    };
111    removexattr_func(path, name)?;
112    Ok(())
113}
114
115pub fn list_path(path: &Path, deref: bool) -> io::Result<XAttrs> {
116    let vec = allocate_loop(|buf| {
117        if deref {
118            rfs::listxattr(path, buf)
119        } else {
120            rfs::llistxattr(path, buf)
121        }
122    })?;
123    Ok(XAttrs {
124        data: vec.into_boxed_slice(),
125        offset: 0,
126    })
127}