@@ -153,6 +153,43 @@ pub enum TryLockError {
153153 WouldBlock,
154154}
155155
156+ /// An object providing access to a directory on the filesystem.
157+ ///
158+ /// Directories are automatically closed when they go out of scope. Errors detected
159+ /// on closing are ignored by the implementation of `Drop`.
160+ ///
161+ /// # Platform-specific behavior
162+ ///
163+ /// On supported systems (including Windows and some UNIX-based OSes), this function acquires a
164+ /// handle/file descriptor for the directory. This allows functions like [`Dir::open_file`] to
165+ /// avoid [TOCTOU] errors when the directory itself is being moved.
166+ ///
167+ /// On other systems, it stores an absolute path (see [`canonicalize()`]). In the latter case, no
168+ /// [TOCTOU] guarantees are made.
169+ ///
170+ /// # Examples
171+ ///
172+ /// Opens a directory and then a file inside it.
173+ ///
174+ /// ```no_run
175+ /// #![feature(dirfd)]
176+ /// use std::{fs::Dir, io};
177+ ///
178+ /// fn main() -> std::io::Result<()> {
179+ /// let dir = Dir::open("foo")?;
180+ /// let mut file = dir.open_file("bar.txt")?;
181+ /// let contents = io::read_to_string(file)?;
182+ /// assert_eq!(contents, "Hello, world!");
183+ /// Ok(())
184+ /// }
185+ /// ```
186+ ///
187+ /// [TOCTOU]: self#time-of-check-to-time-of-use-toctou
188+ #[unstable(feature = "dirfd", issue = "120426")]
189+ pub struct Dir {
190+ inner: fs_imp::Dir,
191+ }
192+
156193/// Metadata information about a file.
157194///
158195/// This structure is returned from the [`metadata`] or
@@ -1555,6 +1592,87 @@ impl Seek for Arc<File> {
15551592 }
15561593}
15571594
1595+ impl Dir {
1596+ /// Attempts to open a directory at `path` in read-only mode.
1597+ ///
1598+ /// # Errors
1599+ ///
1600+ /// This function will return an error if `path` does not point to an existing directory.
1601+ /// Other errors may also be returned according to [`OpenOptions::open`].
1602+ ///
1603+ /// # Examples
1604+ ///
1605+ /// ```no_run
1606+ /// #![feature(dirfd)]
1607+ /// use std::{fs::Dir, io};
1608+ ///
1609+ /// fn main() -> std::io::Result<()> {
1610+ /// let dir = Dir::open("foo")?;
1611+ /// let mut f = dir.open_file("bar.txt")?;
1612+ /// let contents = io::read_to_string(f)?;
1613+ /// assert_eq!(contents, "Hello, world!");
1614+ /// Ok(())
1615+ /// }
1616+ /// ```
1617+ #[unstable(feature = "dirfd", issue = "120426")]
1618+ pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> {
1619+ fs_imp::Dir::open(path.as_ref(), &OpenOptions::new().read(true).0)
1620+ .map(|inner| Self { inner })
1621+ }
1622+
1623+ /// Attempts to open a file in read-only mode relative to this directory.
1624+ ///
1625+ /// # Errors
1626+ ///
1627+ /// This function will return an error if `path` does not point to an existing file.
1628+ /// Other errors may also be returned according to [`OpenOptions::open`].
1629+ ///
1630+ /// # Examples
1631+ ///
1632+ /// ```no_run
1633+ /// #![feature(dirfd)]
1634+ /// use std::{fs::Dir, io};
1635+ ///
1636+ /// fn main() -> std::io::Result<()> {
1637+ /// let dir = Dir::open("foo")?;
1638+ /// let mut f = dir.open_file("bar.txt")?;
1639+ /// let contents = io::read_to_string(f)?;
1640+ /// assert_eq!(contents, "Hello, world!");
1641+ /// Ok(())
1642+ /// }
1643+ /// ```
1644+ #[unstable(feature = "dirfd", issue = "120426")]
1645+ pub fn open_file<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
1646+ self.inner
1647+ .open_file(path.as_ref(), &OpenOptions::new().read(true).0)
1648+ .map(|f| File { inner: f })
1649+ }
1650+ }
1651+
1652+ impl AsInner<fs_imp::Dir> for Dir {
1653+ #[inline]
1654+ fn as_inner(&self) -> &fs_imp::Dir {
1655+ &self.inner
1656+ }
1657+ }
1658+ impl FromInner<fs_imp::Dir> for Dir {
1659+ fn from_inner(f: fs_imp::Dir) -> Dir {
1660+ Dir { inner: f }
1661+ }
1662+ }
1663+ impl IntoInner<fs_imp::Dir> for Dir {
1664+ fn into_inner(self) -> fs_imp::Dir {
1665+ self.inner
1666+ }
1667+ }
1668+
1669+ #[unstable(feature = "dirfd", issue = "120426")]
1670+ impl fmt::Debug for Dir {
1671+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1672+ self.inner.fmt(f)
1673+ }
1674+ }
1675+
15581676impl OpenOptions {
15591677 /// Creates a blank new set of options ready for configuration.
15601678 ///
0 commit comments