Skip to content

Commit a578780

Browse files
committed
Auto merge of #160533 - Qelxiros:dirfd-dirs, r=<try>
dirfd dir operations (3/4) try-job: aarch64-apple-2
2 parents e776960 + c2b76de commit a578780

5 files changed

Lines changed: 276 additions & 7 deletions

File tree

library/std/src/fs.rs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,6 +1578,63 @@ impl Dir {
15781578
.map(|inner| Self { inner })
15791579
}
15801580

1581+
/// Attempts to open a directory at `path` according to `opts`.
1582+
///
1583+
/// This function opens a directory. To open a file instead, see [`File::open`].
1584+
///
1585+
/// # Errors
1586+
///
1587+
/// This function will return an error if `path` does not point to an existing directory.
1588+
/// Other errors may also be returned according to [`OpenOptions::open`].
1589+
///
1590+
/// # Examples
1591+
///
1592+
/// ```no_run
1593+
/// #![feature(dirfd)]
1594+
/// use std::{fs::{Dir, OpenOptions}, io};
1595+
///
1596+
/// fn main() -> std::io::Result<()> {
1597+
/// let dir = Dir::open_with("foo", &OpenOptions::new().read(true))?;
1598+
/// let mut f = dir.open_file("bar.txt")?;
1599+
/// let contents = io::read_to_string(f)?;
1600+
/// assert_eq!(contents, "Hello, world!");
1601+
/// Ok(())
1602+
/// }
1603+
/// ```
1604+
#[unstable(feature = "dirfd", issue = "120426")]
1605+
pub fn open_with<P: AsRef<Path>>(path: P, opts: &OpenOptions) -> io::Result<Self> {
1606+
fs_imp::Dir::open(path.as_ref(), &opts.0).map(|inner| Self { inner })
1607+
}
1608+
1609+
/// Attempts to open a directory at `path` with the minimum permissions for traversal.
1610+
///
1611+
/// The permissions requested by this function are guaranteed to be sufficient to open a child
1612+
/// file or folder, but not necessarily to list all children.
1613+
///
1614+
/// # Errors
1615+
///
1616+
/// This function may return an error according to [`OpenOptions::open`].
1617+
///
1618+
/// # Examples
1619+
///
1620+
/// ```no_run
1621+
/// #![feature(dirfd)]
1622+
/// use std::{fs::Dir, io};
1623+
///
1624+
/// fn main() -> std::io::Result<()> {
1625+
/// let foo = Dir::open_for_traversal("foo")?;
1626+
/// let foobar = foo.open_dir("bar")?;
1627+
/// let mut foobarbaz = foobar.open_file("baz")?;
1628+
/// let contents = io::read_to_string(foobarbaz)?;
1629+
/// assert_eq!(contents, "Hello, world!");
1630+
/// Ok(())
1631+
/// }
1632+
/// ```
1633+
#[unstable(feature = "dirfd", issue = "120426")]
1634+
pub fn open_for_traversal<P: AsRef<Path>>(path: P) -> io::Result<Self> {
1635+
fs_imp::Dir::open_for_traversal(path.as_ref()).map(|inner| Self { inner })
1636+
}
1637+
15811638
/// Queries metadata about the underlying directory.
15821639
///
15831640
/// # Examples
@@ -1719,6 +1776,99 @@ impl Dir {
17191776
) -> io::Result<()> {
17201777
self.inner.rename(from.as_ref(), &to_dir.inner, to.as_ref())
17211778
}
1779+
1780+
/// Attempts to create a directory relative to this directory.
1781+
///
1782+
/// This function interprets `path` relative to the directory provided by `self`. To create a directory
1783+
/// relative to the current working directory, or at an absolute path, see
1784+
/// [`fs::create_dir`][crate::fs::create_dir].
1785+
#[unstable(feature = "dirfd", issue = "120426")]
1786+
pub fn create_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
1787+
self.inner.create_dir(path.as_ref())
1788+
}
1789+
1790+
/// Attempts to open a directory in read-only mode relative to this directory.
1791+
///
1792+
/// This function interprets `path` relative to the directory provided by `self`. To open a directory
1793+
/// relative to the current working directory, or at an absolute path, see [`Dir::open`].
1794+
///
1795+
/// # Errors
1796+
///
1797+
/// This function will return an error if `path` does not point to an existing directory.
1798+
/// Other errors may also be returned according to [`OpenOptions::open`].
1799+
///
1800+
/// # Examples
1801+
///
1802+
/// ```no_run
1803+
/// #![feature(dirfd)]
1804+
/// use std::{fs::Dir};
1805+
///
1806+
/// fn main() -> std::io::Result<()> {
1807+
/// let dir = Dir::open("foo")?;
1808+
/// let foobar = dir.open_dir("bar")?;
1809+
/// Ok(())
1810+
/// }
1811+
/// ```
1812+
#[unstable(feature = "dirfd", issue = "120426")]
1813+
pub fn open_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<Self> {
1814+
self.inner
1815+
.open_dir(path.as_ref(), &OpenOptions::new().read(true).0)
1816+
.map(|inner| Self { inner })
1817+
}
1818+
1819+
/// Attempts to open a directory relative to this directory according to `opts`.
1820+
///
1821+
/// This function interprets `path` relative to the directory provided by `self`. To open a directory
1822+
/// relative to the current working directory, or at an absolute path, see [`Dir::open`].
1823+
///
1824+
/// # Errors
1825+
///
1826+
/// This function will return errors according to [`OpenOptions::open`].
1827+
///
1828+
/// # Examples
1829+
///
1830+
/// ```no_run
1831+
/// #![feature(dirfd)]
1832+
/// use std::fs::{Dir, OpenOptions};
1833+
///
1834+
/// fn main() -> std::io::Result<()> {
1835+
/// let dir = Dir::open("foo")?;
1836+
/// let foobar_w = dir.open_dir_with("bar", &OpenOptions::new().write(true))?;
1837+
/// Ok(())
1838+
/// }
1839+
/// ```
1840+
#[unstable(feature = "dirfd", issue = "120426")]
1841+
pub fn open_dir_with<P: AsRef<Path>>(&self, path: P, opts: &OpenOptions) -> io::Result<Self> {
1842+
self.inner.open_dir(path.as_ref(), &opts.0).map(|inner| Self { inner })
1843+
}
1844+
1845+
/// Attempts to remove a directory relative to this directory.
1846+
///
1847+
/// This function interprets `path` relative to the directory provided by `self`. To remove a directory
1848+
/// relative to the current working directory, or at an absolute path, see
1849+
/// [`fs::remove_dir`][crate::fs::remove_dir].
1850+
///
1851+
/// # Errors
1852+
///
1853+
/// This function will return an error if `path` does not point to an existing directory.
1854+
/// Other errors may also be returned according to [`OpenOptions::open`].
1855+
///
1856+
/// # Examples
1857+
///
1858+
/// ```no_run
1859+
/// #![feature(dirfd)]
1860+
/// use std::{fs::Dir};
1861+
///
1862+
/// fn main() -> std::io::Result<()> {
1863+
/// let dir = Dir::open("foo")?;
1864+
/// dir.remove_dir("bar")?;
1865+
/// Ok(())
1866+
/// }
1867+
/// ```
1868+
#[unstable(feature = "dirfd", issue = "120426")]
1869+
pub fn remove_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
1870+
self.inner.remove_dir(path.as_ref())
1871+
}
17221872
}
17231873

17241874
impl AsInner<fs_imp::Dir> for Dir {

library/std/src/fs/tests.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2756,3 +2756,38 @@ fn test_dir_rename_file() {
27562756
check!(f.read_exact(&mut buf));
27572757
assert_eq!(b"bar", &buf);
27582758
}
2759+
2760+
#[test]
2761+
fn test_dir_remove_dir() {
2762+
let tmpdir = tmpdir();
2763+
check!(fs::create_dir(tmpdir.join("foo")));
2764+
let dir = check!(Dir::open(tmpdir.path()));
2765+
check!(dir.remove_dir("foo"));
2766+
assert!(!matches!(exists(tmpdir.join("foo")), Ok(true)));
2767+
}
2768+
2769+
#[test]
2770+
fn test_dir_create_dir() {
2771+
let tmpdir = tmpdir();
2772+
let dir = check!(Dir::open(tmpdir.path()));
2773+
check!(dir.create_dir("foo"));
2774+
check!(Dir::open(tmpdir.join("foo")));
2775+
}
2776+
2777+
#[test]
2778+
fn test_dir_open_dir() {
2779+
let tmpdir = tmpdir();
2780+
let dir1 = check!(Dir::open(tmpdir.path()));
2781+
check!(dir1.create_dir("foo"));
2782+
let dir2 = check!(Dir::open(tmpdir.path().join("foo")));
2783+
let mut f =
2784+
check!(dir2.open_file_with("bar.txt", &OpenOptions::new().create(true).write(true)));
2785+
check!(f.write(b"baz"));
2786+
check!(f.flush());
2787+
drop(f);
2788+
let dir3 = check!(dir1.open_dir("foo"));
2789+
let mut f = check!(dir3.open_file("bar.txt"));
2790+
let mut buf = [0u8; 3];
2791+
check!(f.read_exact(&mut buf));
2792+
assert_eq!(b"baz", &buf);
2793+
}

library/std/src/sys/fs/common.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![allow(dead_code)] // not used on all platforms
22

3-
use crate::fs::{remove_file, rename};
3+
use crate::fs::{create_dir, remove_dir, remove_file, rename};
44
use crate::io::{self, Error, ErrorKind};
55
use crate::path::{Path, PathBuf};
66
use crate::sys::IntoInner;
@@ -71,6 +71,12 @@ impl Dir {
7171
path.canonicalize().map(|path| Self { path })
7272
}
7373

74+
pub fn open_for_traversal(path: &Path) -> io::Result<Self> {
75+
let mut opts = OpenOptions::new();
76+
opts.read(true);
77+
Self::open(path, &opts)
78+
}
79+
7480
pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
7581
File::open(&self.path.join(path), opts)
7682
}
@@ -86,6 +92,18 @@ impl Dir {
8692
pub fn rename(&self, from: &Path, to_dir: &Self, to: &Path) -> io::Result<()> {
8793
rename(self.path.join(from), to_dir.path.join(to))
8894
}
95+
96+
pub fn create_dir(&self, path: &Path) -> io::Result<()> {
97+
create_dir(self.path.join(path))
98+
}
99+
100+
pub fn open_dir(&self, path: &Path, opts: &OpenOptions) -> io::Result<Self> {
101+
Self::open(&self.path.join(path), opts)
102+
}
103+
104+
pub fn remove_dir(&self, path: &Path) -> io::Result<()> {
105+
remove_dir(path)
106+
}
89107
}
90108

91109
impl fmt::Debug for Dir {

library/std/src/sys/fs/unix/dir.rs

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use libc::{c_int, renameat, unlinkat};
1+
use libc::{c_int, mkdirat, renameat, unlinkat};
22

33
cfg_select! {
44
not(any(
@@ -28,15 +28,29 @@ use crate::sys::helpers::run_path_with_cstr;
2828
use crate::sys::{AsInner, FromInner, IntoInner, cvt, cvt_r};
2929
use crate::{fmt, fs, io};
3030

31+
const 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+
3139
pub struct Dir(OwnedFd);
3240

3341
impl Dir {
3442
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<Self> {
3543
run_path_with_cstr(path, &|path| Self::open_with_c(path, opts))
3644
}
3745

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+
3850
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))
51+
run_path_with_cstr(path.as_ref(), &|path| self.open_file_c(path, opts, 0))
52+
.map(|fd| FileDesc::from_inner(fd))
53+
.map(File)
4054
}
4155

4256
pub fn metadata(&self) -> io::Result<FileAttr> {
@@ -59,7 +73,19 @@ impl Dir {
5973
})
6074
}
6175

62-
pub fn open_with_c(path: &CStr, opts: &OpenOptions) -> io::Result<Self> {
76+
pub fn open_dir(&self, path: &Path, opts: &OpenOptions) -> io::Result<Self> {
77+
run_path_with_cstr(path, &|path| self.open_file_c(path, opts, libc::O_DIRECTORY)).map(Self)
78+
}
79+
80+
pub fn create_dir(&self, path: &Path) -> io::Result<()> {
81+
run_path_with_cstr(path.as_ref(), &|path| self.create_dir_c(path))
82+
}
83+
84+
pub fn remove_dir(&self, path: &Path) -> io::Result<()> {
85+
run_path_with_cstr(path, &|path| self.remove_c(path, true))
86+
}
87+
88+
fn open_with_c(path: &CStr, opts: &OpenOptions) -> io::Result<Self> {
6389
let flags = libc::O_CLOEXEC
6490
| libc::O_DIRECTORY
6591
| opts.get_access_mode()?
@@ -69,15 +95,27 @@ impl Dir {
6995
Ok(Self(unsafe { OwnedFd::from_raw_fd(fd) }))
7096
}
7197

72-
fn open_file_c(&self, path: &CStr, opts: &OpenOptions) -> io::Result<File> {
98+
fn open_traversal_c(path: &CStr) -> io::Result<Self> {
99+
let flags = libc::O_CLOEXEC | libc::O_DIRECTORY | TRAVERSE_DIRECTORY;
100+
let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, 0) })?;
101+
Ok(Self(unsafe { OwnedFd::from_raw_fd(fd) }))
102+
}
103+
104+
fn open_file_c(
105+
&self,
106+
path: &CStr,
107+
opts: &OpenOptions,
108+
extra_flags: c_int,
109+
) -> io::Result<OwnedFd> {
73110
let flags = libc::O_CLOEXEC
74111
| opts.get_access_mode()?
75112
| opts.get_creation_mode()?
76-
| (opts.custom_flags as c_int & !libc::O_ACCMODE);
113+
| (opts.custom_flags as c_int & !libc::O_ACCMODE)
114+
| extra_flags;
77115
let fd = cvt_r(|| unsafe {
78116
openat64(self.0.as_raw_fd(), path.as_ptr(), flags, opts.mode as c_int)
79117
})?;
80-
Ok(File(unsafe { FileDesc::from_raw_fd(fd) }))
118+
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
81119
}
82120

83121
fn remove_c(&self, path: &CStr, remove_dir: bool) -> io::Result<()> {
@@ -97,6 +135,10 @@ impl Dir {
97135
})
98136
.map(|_| ())
99137
}
138+
139+
fn create_dir_c(&self, path: &CStr) -> io::Result<()> {
140+
cvt(unsafe { mkdirat(self.0.as_raw_fd(), path.as_ptr(), 0o777) }).map(|_| ())
141+
}
100142
}
101143

102144
impl fmt::Debug for Dir {

library/std/src/sys/fs/windows/dir.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ impl Dir {
6666
with_native_path(path, &|path| Self::open_with_native(path, opts))
6767
}
6868

69+
pub fn open_for_traversal(path: &Path) -> io::Result<Self> {
70+
let mut opts = OpenOptions::new();
71+
opts.access_mode(c::FILE_TRAVERSE);
72+
with_native_path(path, &|path| Self::open_with_native(path, &opts))
73+
}
74+
6975
pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
7076
// NtCreateFile will fail if given an absolute path and a non-null RootDirectory
7177
if path.is_absolute() {
@@ -87,6 +93,24 @@ impl Dir {
8793
self.rename_native(&from, to_dir, &to, is_dir)
8894
}
8995

96+
pub fn create_dir(&self, path: &Path) -> io::Result<()> {
97+
let mut opts = OpenOptions::new();
98+
opts.read(true);
99+
opts.write(true);
100+
opts.create_new(true);
101+
self.open_dir(path, &opts).map(|_| ())
102+
}
103+
104+
pub fn open_dir(&self, path: &Path, opts: &OpenOptions) -> io::Result<Self> {
105+
let path = to_u16s_without_nul(&path)?;
106+
self.open_file_native(&path, &opts, true).map(|handle| Self { handle })
107+
}
108+
109+
pub fn remove_dir(&self, path: &Path) -> io::Result<()> {
110+
let path = to_u16s_without_nul(&path)?;
111+
self.remove_native(&path, true)
112+
}
113+
90114
fn open_with_native(path: &WCStr, opts: &OpenOptions) -> io::Result<Self> {
91115
let creation = opts.get_creation_mode()?;
92116
let sa = c::SECURITY_ATTRIBUTES {

0 commit comments

Comments
 (0)