1use std::io::Result;
2use std::{
3 ffi::OsStr,
4 os::unix::ffi::OsStrExt,
5 path::{Path, PathBuf},
6};
7
8use cap_tempfile::cap_std::fs::Dir;
9use rustix::buffer::spare_capacity;
10
11use crate::dirext::validate_relpath_no_uplinks;
12
13fn proc_self_path(d: &Dir, path: &Path) -> Result<PathBuf> {
19 use rustix::path::DecInt;
20 use std::os::fd::{AsFd, AsRawFd};
21
22 let path = validate_relpath_no_uplinks(path)?;
24
25 let mut pathbuf = PathBuf::from("/proc/self/fd");
26 pathbuf.push(DecInt::new(d.as_fd().as_raw_fd()));
27 pathbuf.push(path);
28 Ok(pathbuf)
29}
30
31pub(crate) fn impl_getxattr(d: &Dir, path: &Path, key: &OsStr) -> Result<Option<Vec<u8>>> {
32 let path = &proc_self_path(d, path)?;
33
34 let mut buf = Vec::with_capacity(256);
36
37 loop {
38 match rustix::fs::lgetxattr(path, key, spare_capacity(&mut buf)) {
39 Ok(_) => {
40 return Ok(Some(buf));
41 }
42 Err(rustix::io::Errno::NODATA) => {
43 return Ok(None);
44 }
45 Err(rustix::io::Errno::RANGE) => {
46 buf.reserve(buf.capacity().saturating_mul(2));
47 }
48 Err(e) => {
49 return Err(e.into());
50 }
51 }
52 }
53}
54
55#[derive(Debug)]
57#[cfg(any(target_os = "android", target_os = "linux"))]
58pub struct XattrList {
59 buf: Vec<u8>,
65}
66
67#[cfg(any(target_os = "android", target_os = "linux"))]
68impl XattrList {
69 pub fn iter(&self) -> impl Iterator<Item = &'_ std::ffi::OsStr> {
71 self.buf.split(|&v| v == 0).filter_map(|v| {
72 if v.is_empty() {
74 None
75 } else {
76 Some(OsStr::from_bytes(v))
77 }
78 })
79 }
80}
81
82#[cfg(any(target_os = "android", target_os = "linux"))]
83pub(crate) fn impl_listxattrs(d: &Dir, path: &Path) -> Result<XattrList> {
84 let path = &proc_self_path(d, path)?;
85
86 let mut buf = Vec::with_capacity(512);
87
88 loop {
89 match rustix::fs::llistxattr(path, spare_capacity(&mut buf)) {
90 Ok(_) => {
91 return Ok(XattrList { buf });
92 }
93 Err(rustix::io::Errno::RANGE) => {
94 buf.reserve(buf.capacity().saturating_mul(2));
95 }
96 Err(e) => {
97 return Err(e.into());
98 }
99 }
100 }
101}
102
103#[cfg(any(target_os = "android", target_os = "linux"))]
104pub(crate) fn impl_setxattr(d: &Dir, path: &Path, key: &OsStr, value: &[u8]) -> Result<()> {
105 let path = &proc_self_path(d, path)?;
106 rustix::fs::lsetxattr(path, key, value, rustix::fs::XattrFlags::empty())?;
107 Ok(())
108}