1use libc::{c_int, 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
31pub struct Dir(OwnedFd);
32
33impl Dir {
34 pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<Self> {
35 run_path_with_cstr(path, &|path| Self::open_with_c(path, opts))
36 }
37
38 pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
39 run_path_with_cstr(path.as_ref(), &|path| self.open_file_c(path, opts))
40 }
41
42 pub fn metadata(&self) -> io::Result<FileAttr> {
43 let fd = self.0.as_raw_fd();
45 let f = core::mem::ManuallyDrop::new(File(
46 unsafe { FileDesc::from_raw_fd(fd) },
48 ));
49 f.file_attr()
50 }
51
52 pub fn remove_file(&self, path: &Path) -> io::Result<()> {
53 run_path_with_cstr(path, &|path| self.remove_c(path, false))
54 }
55
56 pub fn rename(&self, from: &Path, to_dir: &Self, to: &Path) -> io::Result<()> {
57 run_path_with_cstr(from, &|from| {
58 run_path_with_cstr(to, &|to| self.rename_c(from, to_dir, to))
59 })
60 }
61
62 pub fn open_with_c(path: &CStr, opts: &OpenOptions) -> io::Result<Self> {
63 let flags = libc::O_CLOEXEC
64 | libc::O_DIRECTORY
65 | opts.get_access_mode()?
66 | opts.get_creation_mode()?
67 | (opts.custom_flags as c_int & !libc::O_ACCMODE);
68 let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
69 Ok(Self(unsafe { OwnedFd::from_raw_fd(fd) }))
70 }
71
72 fn open_file_c(&self, path: &CStr, opts: &OpenOptions) -> io::Result<File> {
73 let flags = libc::O_CLOEXEC
74 | opts.get_access_mode()?
75 | opts.get_creation_mode()?
76 | (opts.custom_flags as c_int & !libc::O_ACCMODE);
77 let fd = cvt_r(|| unsafe {
78 openat64(self.0.as_raw_fd(), path.as_ptr(), flags, opts.mode as c_int)
79 })?;
80 Ok(File(unsafe { FileDesc::from_raw_fd(fd) }))
81 }
82
83 fn remove_c(&self, path: &CStr, remove_dir: bool) -> io::Result<()> {
84 cvt(unsafe {
85 unlinkat(
86 self.0.as_raw_fd(),
87 path.as_ptr(),
88 if remove_dir { libc::AT_REMOVEDIR } else { 0 },
89 )
90 })
91 .map(|_| ())
92 }
93
94 fn rename_c(&self, from: &CStr, to_dir: &Self, to: &CStr) -> io::Result<()> {
95 cvt(unsafe {
96 renameat(self.0.as_raw_fd(), from.as_ptr(), to_dir.0.as_raw_fd(), to.as_ptr())
97 })
98 .map(|_| ())
99 }
100}
101
102impl fmt::Debug for Dir {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 let fd = self.0.as_raw_fd();
105 let mut b = debug_path_fd(fd, f, "Dir");
106 b.finish()
107 }
108}
109
110#[unstable(feature = "dirfd", issue = "120426")]
111impl AsRawFd for fs::Dir {
112 fn as_raw_fd(&self) -> RawFd {
113 self.as_inner().0.as_raw_fd()
114 }
115}
116
117#[unstable(feature = "dirfd", issue = "120426")]
118impl IntoRawFd for fs::Dir {
119 fn into_raw_fd(self) -> RawFd {
120 self.into_inner().0.into_raw_fd()
121 }
122}
123
124#[unstable(feature = "dirfd", issue = "120426")]
125impl FromRawFd for fs::Dir {
126 unsafe fn from_raw_fd(fd: RawFd) -> Self {
127 Self::from_inner(Dir(unsafe { FromRawFd::from_raw_fd(fd) }))
128 }
129}
130
131#[unstable(feature = "dirfd", issue = "120426")]
132impl AsFd for fs::Dir {
133 fn as_fd(&self) -> BorrowedFd<'_> {
134 self.as_inner().0.as_fd()
135 }
136}
137
138#[unstable(feature = "dirfd", issue = "120426")]
139impl From<fs::Dir> for OwnedFd {
140 fn from(value: fs::Dir) -> Self {
141 value.into_inner().0
142 }
143}
144
145#[unstable(feature = "dirfd", issue = "120426")]
146impl From<OwnedFd> for fs::Dir {
147 fn from(value: OwnedFd) -> Self {
148 Self::from_inner(Dir(value))
149 }
150}