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 = "android",
8 target_os = "hurd",
9 )) => {
10 use libc::{open as open64, openat as openat64};
11 }
12 _ => {
13 use libc::{open64, openat64};
14 }
15}
16
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};
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(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 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 fn open_with_c(path: &CStr, opts: &OpenOptions) -> io::Result<Self> {
93 let flags = libc::O_CLOEXEC
94 | libc::O_DIRECTORY
95 | opts.get_access_mode()?
96 | opts.get_creation_mode()?
97 | (opts.custom_flags as c_int & !libc::O_ACCMODE);
98 let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
99 Ok(Self(unsafe { OwnedFd::from_raw_fd(fd) }))
100 }
101
102 fn open_traversal_c(path: &CStr) -> io::Result<Self> {
103 let flags = libc::O_CLOEXEC | libc::O_DIRECTORY | TRAVERSE_DIRECTORY;
104 let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, 0) })?;
105 Ok(Self(unsafe { OwnedFd::from_raw_fd(fd) }))
106 }
107
108 fn open_file_c(
109 &self,
110 path: &CStr,
111 opts: &OpenOptions,
112 extra_flags: c_int,
113 ) -> io::Result<OwnedFd> {
114 let flags = libc::O_CLOEXEC
115 | opts.get_access_mode()?
116 | opts.get_creation_mode()?
117 | (opts.custom_flags as c_int & !libc::O_ACCMODE)
118 | extra_flags;
119 let fd = cvt_r(|| unsafe {
120 openat64(self.0.as_raw_fd(), path.as_ptr(), flags, opts.mode as c_int)
121 })?;
122 Ok(unsafe { OwnedFd::from_raw_fd(fd) })
123 }
124
125 fn remove_c(&self, path: &CStr, remove_dir: bool) -> io::Result<()> {
126 cvt(unsafe {
127 unlinkat(
128 self.0.as_raw_fd(),
129 path.as_ptr(),
130 if remove_dir { libc::AT_REMOVEDIR } else { 0 },
131 )
132 })
133 .map(|_| ())
134 }
135
136 fn rename_c(&self, from: &CStr, to_dir: &Self, to: &CStr) -> io::Result<()> {
137 cvt(unsafe {
138 renameat(self.0.as_raw_fd(), from.as_ptr(), to_dir.0.as_raw_fd(), to.as_ptr())
139 })
140 .map(|_| ())
141 }
142
143 fn create_dir_c(&self, path: &CStr) -> io::Result<()> {
144 cvt(unsafe { mkdirat(self.0.as_raw_fd(), path.as_ptr(), 0o777) }).map(|_| ())
145 }
146}
147
148impl fmt::Debug for Dir {
149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150 let fd = self.0.as_raw_fd();
151 let mut b = debug_path_fd(fd, f, "Dir");
152 b.finish()
153 }
154}
155
156#[unstable(feature = "dirfd", issue = "120426")]
157impl AsRawFd for fs::Dir {
158 fn as_raw_fd(&self) -> RawFd {
159 self.as_inner().0.as_raw_fd()
160 }
161}
162
163#[unstable(feature = "dirfd", issue = "120426")]
164impl IntoRawFd for fs::Dir {
165 fn into_raw_fd(self) -> RawFd {
166 self.into_inner().0.into_raw_fd()
167 }
168}
169
170#[unstable(feature = "dirfd", issue = "120426")]
171impl FromRawFd for fs::Dir {
172 unsafe fn from_raw_fd(fd: RawFd) -> Self {
173 Self::from_inner(Dir(unsafe { FromRawFd::from_raw_fd(fd) }))
174 }
175}
176
177#[unstable(feature = "dirfd", issue = "120426")]
178impl AsFd for fs::Dir {
179 fn as_fd(&self) -> BorrowedFd<'_> {
180 self.as_inner().0.as_fd()
181 }
182}
183
184#[unstable(feature = "dirfd", issue = "120426")]
185impl From<fs::Dir> for OwnedFd {
186 fn from(value: fs::Dir) -> Self {
187 value.into_inner().0
188 }
189}
190
191#[unstable(feature = "dirfd", issue = "120426")]
192impl From<OwnedFd> for fs::Dir {
193 fn from(value: OwnedFd) -> Self {
194 Self::from_inner(Dir(value))
195 }
196}