1use std::fs;
2use std::io;
3use std::io::Read;
4use std::path::Path;
5
6use cap_std::fs::Dir;
7use cap_tempfile::cap_std;
8use rustix::fd::AsFd;
9use rustix::fd::BorrowedFd;
10use rustix::fs::OFlags;
11use rustix::fs::ResolveFlags;
12use rustix::path::Arg;
13
14pub(crate) fn open_beneath_rdonly(start: &BorrowedFd, path: &Path) -> io::Result<fs::File> {
15 let r = path.into_with_c_str(|path_c_str| 'start: loop {
18 match rustix::fs::openat2(
19 start,
20 path_c_str,
21 OFlags::CLOEXEC | OFlags::RDONLY,
22 rustix::fs::Mode::empty(),
23 ResolveFlags::IN_ROOT | ResolveFlags::NO_MAGICLINKS,
24 ) {
25 Ok(file) => {
26 return Ok(file);
27 }
28 Err(rustix::io::Errno::AGAIN | rustix::io::Errno::INTR) => {
29 continue 'start;
30 }
31 Err(e) => {
32 return Err(e);
33 }
34 }
35 })?;
36 Ok(r.into())
37}
38
39#[derive(Debug)]
55pub struct RootDir(Dir);
56
57impl RootDir {
58 pub fn new(src: &Dir, path: impl AsRef<Path>) -> io::Result<Self> {
60 src.open_dir(path).map(Self)
61 }
62
63 pub fn open_ambient_root(
65 path: impl AsRef<Path>,
66 authority: cap_std::AmbientAuthority,
67 ) -> io::Result<Self> {
68 Dir::open_ambient_dir(path, authority).map(Self)
69 }
70
71 pub fn open(&self, path: impl AsRef<Path>) -> io::Result<fs::File> {
73 let path = path.as_ref();
74 open_beneath_rdonly(&self.0.as_fd(), path)
75 }
76
77 pub fn open_optional(&self, path: impl AsRef<Path>) -> io::Result<Option<fs::File>> {
79 crate::dirext::map_optional(self.open(path))
80 }
81
82 pub fn read(&self, path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
84 let mut f = self.open(path.as_ref())?;
85 let mut r = Vec::new();
86 f.read_to_end(&mut r)?;
87 Ok(r)
88 }
89
90 pub fn read_to_string(&self, path: impl AsRef<Path>) -> io::Result<String> {
92 let mut f = self.open(path.as_ref())?;
93 let mut s = String::new();
94 f.read_to_string(&mut s)?;
95 Ok(s)
96 }
97
98 pub fn entries(&self) -> io::Result<cap_std::fs::ReadDir> {
100 self.0.entries()
101 }
102
103 pub fn read_dir(&self, path: impl AsRef<Path>) -> io::Result<cap_std::fs::ReadDir> {
105 self.0.read_dir(path.as_ref())
106 }
107
108 pub fn reopen_cap_std(&self) -> io::Result<Dir> {
111 Dir::reopen_dir(&self.0.as_fd())
112 }
113}
114
115impl From<Dir> for RootDir {
116 fn from(dir: Dir) -> Self {
117 Self(dir)
118 }
119}