1use libc::{c_int, renameat, unlinkat};
2
3cfg_select! {
4 not(
5 any(
6 all(target_os = "linux", not(target_env = "musl")),
7 target_os = "l4re",
8 target_os = "android",
9 target_os = "hurd",
10 )
11 ) => {
12 use libc::{open as open64, openat as openat64};
13 }
14 _ => {
15 use libc::{open64, openat64};
16 }
17}
18
19use crate::ffi::CStr;
20use crate::os::fd::{AsFd, BorrowedFd, IntoRawFd, OwnedFd, RawFd};
21#[cfg(target_family = "unix")]
22use crate::os::unix::io::{AsRawFd, FromRawFd};
23#[cfg(target_os = "wasi")]
24use crate::os::wasi::io::{AsRawFd, FromRawFd};
25use crate::path::Path;
26use crate::sys::fd::FileDesc;
27use crate::sys::fs::OpenOptions;
28use crate::sys::fs::unix::{File, FileAttr, debug_path_fd};
29use crate::sys::helpers::run_path_with_cstr;
30use crate::sys::{AsInner, FromInner, IntoInner, cvt, cvt_r};
31use crate::{fmt, fs, io};
32
33pub struct Dir(OwnedFd);
34
35impl Dir {
36 pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<Self> {
37 run_path_with_cstr(path, &|path| Self::open_with_c(path, opts))
38 }
39
40 pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
41 run_path_with_cstr(path.as_ref(), &|path| self.open_file_c(path, &opts))
42 }
43
44 pub fn metadata(&self) -> io::Result<FileAttr> {
45 let fd = self.0.as_raw_fd();
47 let f = core::mem::ManuallyDrop::new(File(
48 unsafe { FileDesc::from_raw_fd(fd) },
50 ));
51 f.file_attr()
52 }
53
54 pub fn remove_file(&self, path: &Path) -> io::Result<()> {
55 run_path_with_cstr(path, &|path| self.remove_c(path, false))
56 }
57
58 pub fn rename(&self, from: &Path, to_dir: &Self, to: &Path) -> io::Result<()> {
59 run_path_with_cstr(from, &|from| {
60 run_path_with_cstr(to, &|to| self.rename_c(from, to_dir, to))
61 })
62 }
63
64 pub fn open_with_c(path: &CStr, opts: &OpenOptions) -> io::Result<Self> {
65 let flags = libc::O_CLOEXEC
66 | libc::O_DIRECTORY
67 | opts.get_access_mode()?
68 | opts.get_creation_mode()?
69 | (opts.custom_flags as c_int & !libc::O_ACCMODE);
70 let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
71 Ok(Self(unsafe { OwnedFd::from_raw_fd(fd) }))
72 }
73
74 fn open_file_c(&self, path: &CStr, opts: &OpenOptions) -> io::Result<File> {
75 let flags = libc::O_CLOEXEC
76 | opts.get_access_mode()?
77 | opts.get_creation_mode()?
78 | (opts.custom_flags as c_int & !libc::O_ACCMODE);
79 let fd = cvt_r(|| unsafe {
80 openat64(self.0.as_raw_fd(), path.as_ptr(), flags, opts.mode as c_int)
81 })?;
82 Ok(File(unsafe { FileDesc::from_raw_fd(fd) }))
83 }
84
85 fn remove_c(&self, path: &CStr, remove_dir: bool) -> io::Result<()> {
86 cvt(unsafe {
87 unlinkat(
88 self.0.as_raw_fd(),
89 path.as_ptr(),
90 if remove_dir { libc::AT_REMOVEDIR } else { 0 },
91 )
92 })
93 .map(|_| ())
94 }
95
96 fn rename_c(&self, from: &CStr, to_dir: &Self, to: &CStr) -> io::Result<()> {
97 cvt(unsafe {
98 renameat(self.0.as_raw_fd(), from.as_ptr(), to_dir.0.as_raw_fd(), to.as_ptr())
99 })
100 .map(|_| ())
101 }
102}
103
104impl fmt::Debug for Dir {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 let fd = self.0.as_raw_fd();
107 let mut b = debug_path_fd(fd, f, "Dir");
108 b.finish()
109 }
110}
111
112#[unstable(feature = "dirfd", issue = "120426")]
113impl AsRawFd for fs::Dir {
114 fn as_raw_fd(&self) -> RawFd {
115 self.as_inner().0.as_raw_fd()
116 }
117}
118
119#[unstable(feature = "dirfd", issue = "120426")]
120impl IntoRawFd for fs::Dir {
121 fn into_raw_fd(self) -> RawFd {
122 self.into_inner().0.into_raw_fd()
123 }
124}
125
126#[unstable(feature = "dirfd", issue = "120426")]
127impl FromRawFd for fs::Dir {
128 unsafe fn from_raw_fd(fd: RawFd) -> Self {
129 Self::from_inner(Dir(unsafe { FromRawFd::from_raw_fd(fd) }))
130 }
131}
132
133#[unstable(feature = "dirfd", issue = "120426")]
134impl AsFd for fs::Dir {
135 fn as_fd(&self) -> BorrowedFd<'_> {
136 self.as_inner().0.as_fd()
137 }
138}
139
140#[unstable(feature = "dirfd", issue = "120426")]
141impl From<fs::Dir> for OwnedFd {
142 fn from(value: fs::Dir) -> Self {
143 value.into_inner().0
144 }
145}
146
147#[unstable(feature = "dirfd", issue = "120426")]
148impl From<OwnedFd> for fs::Dir {
149 fn from(value: OwnedFd) -> Self {
150 Self::from_inner(Dir(value))
151 }
152}