Skip to main content

std/sys/fs/unix/
dir.rs

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        // Reuse the implementation for files, which should work for all FDs.
62        let fd = self.0.as_raw_fd();
63        let f = core::mem::ManuallyDrop::new(File(
64            // SAFETY: we borrowed `self` so the FD will not be closed while this function runs.
65            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, /* remove_dir */ 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, /* remove_dir */ 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, /* symlink_nofollow */ 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, /* symlink_nofollow */ 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}