1use libc::{c_int, mkdirat, renameat, unlinkat};
2
3cfg_select! {
4 not(any(
5 all(target_os = "linux", not(target_env = "musl")),
6 target_os = "l4re",
7 target_os = "hurd",
8 )) => {
9 use libc::{fstatat as fstatat64, openat as openat64};
10 }
11 _ => {
12 use libc::{fstatat64, openat64};
13 }
14}
15
16use super::{open64, stat64};
17use crate::ffi::CStr;
18use crate::os::fd::{AsFd, BorrowedFd, IntoRawFd, OwnedFd, RawFd};
19#[cfg(target_family = "unix")]
20use crate::os::unix::io::{AsRawFd, FromRawFd};
21#[cfg(target_os = "wasi")]
22use crate::os::wasi::io::{AsRawFd, FromRawFd};
23use crate::path::Path;
24use crate::sys::fd::FileDesc;
25use crate::sys::fs::OpenOptions;
26use crate::sys::fs::unix::{File, FileAttr, debug_path_fd};
27use crate::sys::helpers::run_path_with_cstr;
28use crate::sys::{AsInner, FromInner, IntoInner, cvt, cvt_r};
29use crate::{fmt, fs, io, mem};
30
31const TRAVERSE_DIRECTORY: i32 =
32 cfg_select! {
33 any(target_os = "freebsd", target_os = "aix") => libc::O_EXEC,
34 any(target_os = "linux", target_os = "android", target_os = "l4re") => libc::O_PATH,
35 target_os = "illumos" => libc::O_SEARCH,
36 _ => libc::O_RDONLY,
37 };
38
39pub struct Dir(pub(super) OwnedFd);
40
41impl Dir {
42 pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<Self> {
43 run_path_with_cstr(path, &|path| Self::open_with_c(path, opts))
44 }
45
46 pub fn open_for_traversal(path: &Path) -> io::Result<Self> {
47 run_path_with_cstr(path, &|path| Self::open_traversal_c(path))
48 }
49
50 pub fn duplicate(&self) -> io::Result<Self> {
51 Ok(Self(self.0.try_clone()?))
52 }
53
54 pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
55 run_path_with_cstr(path.as_ref(), &|path| self.open_file_c(path, opts, 0))
56 .map(FileDesc::from_inner)
57 .map(File)
58 }
59
60 pub fn self_metadata(&self) -> io::Result<FileAttr> {
61 let fd = self.0.as_raw_fd();
63 let f = core::mem::ManuallyDrop::new(File(
64 unsafe { FileDesc::from_raw_fd(fd) },
66 ));
67 f.file_attr()
68 }
69
70 pub fn remove_file(&self, path: &Path) -> io::Result<()> {
71 run_path_with_cstr(path, &|path| self.remove_c(path, false))
72 }
73
74 pub fn rename(&self, from: &Path, to_dir: &Self, to: &Path) -> io::Result<()> {
75 run_path_with_cstr(from, &|from| {
76 run_path_with_cstr(to, &|to| self.rename_c(from, to_dir, to))
77 })
78 }
79
80 pub fn open_dir(&self, path: &Path, opts: &OpenOptions) -> io::Result<Self> {
81 run_path_with_cstr(path, &|path| self.open_file_c(path, opts, libc::O_DIRECTORY)).map(Self)
82 }
83
84 pub fn create_dir(&self, path: &Path) -> io::Result<()> {
85 run_path_with_cstr(path.as_ref(), &|path| self.create_dir_c(path))
86 }
87
88 pub fn remove_dir(&self, path: &Path) -> io::Result<()> {
89 run_path_with_cstr(path, &|path| self.remove_c(path, true))
90 }
91
92 pub fn metadata(&self, path: &Path) -> io::Result<FileAttr> {
93 run_path_with_cstr(path, &|path| {
94 self.metadata_c(path, false)
95 })
96 }
97
98 pub fn symlink_metadata(&self, path: &Path) -> io::Result<FileAttr> {
99 run_path_with_cstr(path, &|path| self.metadata_c(path, true))
100 }
101
102 fn open_with_c(path: &CStr, opts: &OpenOptions) -> io::Result<Self> {
103 let flags = libc::O_CLOEXEC
104 | libc::O_DIRECTORY
105 | opts.get_access_mode()?
106 | opts.get_creation_mode()?
107 | (opts.custom_flags as c_int & !libc::O_ACCMODE);
108 let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
109 Ok(Self(unsafe { OwnedFd::from_raw_fd(fd) }))
110 }
111
112 fn open_traversal_c(path: &CStr) -> io::Result<Self> {
113 let flags = libc::O_CLOEXEC | libc::O_DIRECTORY | TRAVERSE_DIRECTORY;
114 let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, 0) })?;
115 Ok(Self(unsafe { OwnedFd::from_raw_fd(fd) }))
116 }
117
118 fn open_file_c(
119 &self,
120 path: &CStr,
121 opts: &OpenOptions,
122 extra_flags: c_int,
123 ) -> io::Result<OwnedFd> {
124 let flags = libc::O_CLOEXEC
125 | opts.get_access_mode()?
126 | opts.get_creation_mode()?
127 | (opts.custom_flags as c_int & !libc::O_ACCMODE)
128 | extra_flags;
129 let fd = cvt_r(|| unsafe {
130 openat64(self.0.as_raw_fd(), path.as_ptr(), flags, opts.mode as c_int)
131 })?;
132 Ok(unsafe { OwnedFd::from_raw_fd(fd) })
133 }
134
135 fn remove_c(&self, path: &CStr, remove_dir: bool) -> io::Result<()> {
136 cvt(unsafe {
137 unlinkat(
138 self.0.as_raw_fd(),
139 path.as_ptr(),
140 if remove_dir { libc::AT_REMOVEDIR } else { 0 },
141 )
142 })
143 .map(|_| ())
144 }
145
146 fn rename_c(&self, from: &CStr, to_dir: &Self, to: &CStr) -> io::Result<()> {
147 cvt(unsafe {
148 renameat(self.0.as_raw_fd(), from.as_ptr(), to_dir.0.as_raw_fd(), to.as_ptr())
149 })
150 .map(|_| ())
151 }
152
153 fn create_dir_c(&self, path: &CStr) -> io::Result<()> {
154 cvt(unsafe { mkdirat(self.0.as_raw_fd(), path.as_ptr(), 0o777) }).map(|_| ())
155 }
156
157 pub(super) fn metadata_c(&self, path: &CStr, symlink_nofollow: bool) -> io::Result<FileAttr> {
158 let fd = self.0.as_raw_fd();
159 let flag = if symlink_nofollow { libc::AT_SYMLINK_NOFOLLOW } else { 0 };
160
161 {
if let Some(ret) =
unsafe {
super::try_statx(fd, path.as_ptr(),
flag | libc::AT_STATX_SYNC_AS_STAT,
libc::STATX_BASIC_STATS | libc::STATX_BTIME)
} {
return ret;
}
}cfg_has_statx! {
162 if let Some(ret) = unsafe { super::try_statx(
163 fd,
164 path.as_ptr(),
165 flag | libc::AT_STATX_SYNC_AS_STAT,
166 libc::STATX_BASIC_STATS | libc::STATX_BTIME,
167 ) } {
168 return ret;
169 }
170 }
171
172 let mut stat: stat64 = unsafe { mem::zeroed() };
173 cvt(unsafe { fstatat64(fd, path.as_ptr(), &mut stat, flag) })?;
174 Ok(FileAttr::from_stat64(stat))
175 }
176}
177
178impl fmt::Debug for Dir {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 let fd = self.0.as_raw_fd();
181 let mut b = debug_path_fd(fd, f, "Dir");
182 b.finish()
183 }
184}
185
186#[unstable(feature = "dirfd", issue = "120426")]
187impl AsRawFd for fs::Dir {
188 fn as_raw_fd(&self) -> RawFd {
189 self.as_inner().0.as_raw_fd()
190 }
191}
192
193#[unstable(feature = "dirfd", issue = "120426")]
194impl IntoRawFd for fs::Dir {
195 fn into_raw_fd(self) -> RawFd {
196 self.into_inner().0.into_raw_fd()
197 }
198}
199
200#[unstable(feature = "dirfd", issue = "120426")]
201impl FromRawFd for fs::Dir {
202 unsafe fn from_raw_fd(fd: RawFd) -> Self {
203 Self::from_inner(Dir(unsafe { FromRawFd::from_raw_fd(fd) }))
204 }
205}
206
207#[unstable(feature = "dirfd", issue = "120426")]
208impl AsFd for fs::Dir {
209 fn as_fd(&self) -> BorrowedFd<'_> {
210 self.as_inner().0.as_fd()
211 }
212}
213
214#[unstable(feature = "dirfd", issue = "120426")]
215impl From<fs::Dir> for OwnedFd {
216 fn from(value: fs::Dir) -> Self {
217 value.into_inner().0
218 }
219}
220
221#[unstable(feature = "dirfd", issue = "120426")]
222impl From<OwnedFd> for fs::Dir {
223 fn from(value: OwnedFd) -> Self {
224 Self::from_inner(Dir(value))
225 }
226}