Skip to content

Commit 796e04e

Browse files
Auto merge of #150679 - Qelxiros:dirfd-files, r=<try>
dirfd file operations (2/4) try-job: x86_64-msvc-*
2 parents e29fcf4 + 003285e commit 796e04e

File tree

7 files changed

+310
-13
lines changed

7 files changed

+310
-13
lines changed

‎library/std/src/fs.rs‎

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,6 +1646,89 @@ impl Dir {
16461646
.open_file(path.as_ref(), &OpenOptions::new().read(true).0)
16471647
.map(|f| File { inner: f })
16481648
}
1649+
1650+
/// Attempts to open a file according to `opts` relative to this directory.
1651+
///
1652+
/// # Errors
1653+
///
1654+
/// This function will return an error if `path` does not point to an existing file.
1655+
/// Other errors may also be returned according to [`OpenOptions::open`].
1656+
///
1657+
/// # Examples
1658+
///
1659+
/// ```no_run
1660+
/// #![feature(dirfd)]
1661+
/// use std::{fs::{Dir, OpenOptions}, io::{self, Write}};
1662+
///
1663+
/// fn main() -> io::Result<()> {
1664+
/// let dir = Dir::open("foo")?;
1665+
/// let mut opts = OpenOptions::new();
1666+
/// opts.read(true).write(true);
1667+
/// let mut f = dir.open_file_with("bar.txt", &opts)?;
1668+
/// f.write(b"Hello, world!")?;
1669+
/// let contents = io::read_to_string(f)?;
1670+
/// assert_eq!(contents, "Hello, world!");
1671+
/// Ok(())
1672+
/// }
1673+
/// ```
1674+
#[unstable(feature = "dirfd", issue = "120426")]
1675+
pub fn open_file_with<P: AsRef<Path>>(&self, path: P, opts: &OpenOptions) -> io::Result<File> {
1676+
self.inner.open_file(path.as_ref(), &opts.0).map(|f| File { inner: f })
1677+
}
1678+
1679+
/// Attempts to remove a file relative to this directory.
1680+
///
1681+
/// # Errors
1682+
///
1683+
/// This function will return an error if `path` does not point to an existing file.
1684+
/// Other errors may also be returned according to [`OpenOptions::open`].
1685+
///
1686+
/// # Examples
1687+
///
1688+
/// ```no_run
1689+
/// #![feature(dirfd)]
1690+
/// use std::fs::Dir;
1691+
///
1692+
/// fn main() -> std::io::Result<()> {
1693+
/// let dir = Dir::open("foo")?;
1694+
/// dir.remove_file("bar.txt")?;
1695+
/// Ok(())
1696+
/// }
1697+
/// ```
1698+
#[unstable(feature = "dirfd", issue = "120426")]
1699+
pub fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
1700+
self.inner.remove_file(path.as_ref())
1701+
}
1702+
1703+
/// Attempts to rename a file or directory relative to this directory to a new name, replacing
1704+
/// the destination file if present.
1705+
///
1706+
/// # Errors
1707+
///
1708+
/// This function will return an error if `from` does not point to an existing file or directory.
1709+
/// Other errors may also be returned according to [`OpenOptions::open`].
1710+
///
1711+
/// # Examples
1712+
///
1713+
/// ```no_run
1714+
/// #![feature(dirfd)]
1715+
/// use std::fs::Dir;
1716+
///
1717+
/// fn main() -> std::io::Result<()> {
1718+
/// let dir = Dir::open("foo")?;
1719+
/// dir.rename("bar.txt", &dir, "quux.txt")?;
1720+
/// Ok(())
1721+
/// }
1722+
/// ```
1723+
#[unstable(feature = "dirfd", issue = "120426")]
1724+
pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(
1725+
&self,
1726+
from: P,
1727+
to_dir: &Self,
1728+
to: Q,
1729+
) -> io::Result<()> {
1730+
self.inner.rename(from.as_ref(), &to_dir.inner, to.as_ref())
1731+
}
16491732
}
16501733

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

‎library/std/src/fs/tests.rs‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use rand::RngCore;
33
#[cfg(not(miri))]
44
use super::Dir;
55
use crate::assert_matches::assert_matches;
6+
#[cfg(not(miri))]
7+
use crate::fs::exists;
68
use crate::fs::{self, File, FileTimes, OpenOptions, TryLockError};
79
#[cfg(not(miri))]
810
use crate::io;
@@ -2496,3 +2498,50 @@ fn test_dir_read_file() {
24962498
let buf = check!(io::read_to_string(f));
24972499
assert_eq!("bar", &buf);
24982500
}
2501+
2502+
#[test]
2503+
// FIXME: libc calls fail on miri
2504+
#[cfg(not(miri))]
2505+
fn test_dir_write_file() {
2506+
let tmpdir = tmpdir();
2507+
let dir = check!(Dir::open(tmpdir.path()));
2508+
let mut f = check!(dir.open_file_with("foo.txt", &OpenOptions::new().write(true).create(true)));
2509+
check!(f.write(b"bar"));
2510+
check!(f.flush());
2511+
drop(f);
2512+
let mut f = check!(File::open(tmpdir.join("foo.txt")));
2513+
let mut buf = [0u8; 3];
2514+
check!(f.read_exact(&mut buf));
2515+
assert_eq!(b"bar", &buf);
2516+
}
2517+
2518+
#[test]
2519+
// FIXME: libc calls fail on miri
2520+
#[cfg(not(miri))]
2521+
fn test_dir_remove_file() {
2522+
let tmpdir = tmpdir();
2523+
let mut f = check!(File::create(tmpdir.join("foo.txt")));
2524+
check!(f.write(b"bar"));
2525+
check!(f.flush());
2526+
drop(f);
2527+
let dir = check!(Dir::open(tmpdir.path()));
2528+
check!(dir.remove_file("foo.txt"));
2529+
assert!(!matches!(exists(tmpdir.join("foo.txt")), Ok(true)));
2530+
}
2531+
2532+
#[test]
2533+
// FIXME: libc calls fail on miri
2534+
#[cfg(not(miri))]
2535+
fn test_dir_rename_file() {
2536+
let tmpdir = tmpdir();
2537+
let mut f = check!(File::create(tmpdir.join("foo.txt")));
2538+
check!(f.write(b"bar"));
2539+
check!(f.flush());
2540+
drop(f);
2541+
let dir = check!(Dir::open(tmpdir.path()));
2542+
check!(dir.rename("foo.txt", &dir, "baz.txt"));
2543+
let mut f = check!(File::open(tmpdir.join("baz.txt")));
2544+
let mut buf = [0u8; 3];
2545+
check!(f.read_exact(&mut buf));
2546+
assert_eq!(b"bar", &buf);
2547+
}

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

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

3+
use crate::fs::{remove_file, rename};
34
use crate::io::{self, Error, ErrorKind};
45
use crate::path::{Path, PathBuf};
56
use crate::sys::fs::{File, OpenOptions};
@@ -72,6 +73,14 @@ impl Dir {
7273
pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
7374
File::open(&self.path.join(path), &opts)
7475
}
76+
77+
pub fn remove_file(&self, path: &Path) -> io::Result<()> {
78+
remove_file(self.path.join(path))
79+
}
80+
81+
pub fn rename(&self, from: &Path, to_dir: &Self, to: &Path) -> io::Result<()> {
82+
rename(self.path.join(from), to_dir.path.join(to))
83+
}
7584
}
7685

7786
impl fmt::Debug for Dir {

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

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

33
cfg_select! {
44
not(
@@ -27,7 +27,7 @@ use crate::sys::fd::FileDesc;
2727
use crate::sys::fs::OpenOptions;
2828
use crate::sys::fs::unix::{File, debug_path_fd};
2929
use crate::sys::helpers::run_path_with_cstr;
30-
use crate::sys::{AsInner, FromInner, IntoInner, cvt_r};
30+
use crate::sys::{AsInner, FromInner, IntoInner, cvt, cvt_r};
3131
use crate::{fmt, fs, io};
3232

3333
pub struct Dir(OwnedFd);
@@ -41,6 +41,16 @@ impl Dir {
4141
run_path_with_cstr(path.as_ref(), &|path| self.open_file_c(path, &opts))
4242
}
4343

44+
pub fn remove_file(&self, path: &Path) -> io::Result<()> {
45+
run_path_with_cstr(path, &|path| self.remove_c(path, false))
46+
}
47+
48+
pub fn rename(&self, from: &Path, to_dir: &Self, to: &Path) -> io::Result<()> {
49+
run_path_with_cstr(from, &|from| {
50+
run_path_with_cstr(to, &|to| self.rename_c(from, to_dir, to))
51+
})
52+
}
53+
4454
pub fn open_with_c(path: &CStr, opts: &OpenOptions) -> io::Result<Self> {
4555
let flags = libc::O_CLOEXEC
4656
| libc::O_DIRECTORY
@@ -61,6 +71,24 @@ impl Dir {
6171
})?;
6272
Ok(File(unsafe { FileDesc::from_raw_fd(fd) }))
6373
}
74+
75+
fn remove_c(&self, path: &CStr, remove_dir: bool) -> io::Result<()> {
76+
cvt(unsafe {
77+
unlinkat(
78+
self.0.as_raw_fd(),
79+
path.as_ptr(),
80+
if remove_dir { libc::AT_REMOVEDIR } else { 0 },
81+
)
82+
})
83+
.map(|_| ())
84+
}
85+
86+
fn rename_c(&self, from: &CStr, to_dir: &Self, to: &CStr) -> io::Result<()> {
87+
cvt(unsafe {
88+
renameat(self.0.as_raw_fd(), from.as_ptr(), to_dir.0.as_raw_fd(), to.as_ptr())
89+
})
90+
.map(|_| ())
91+
}
6492
}
6593

6694
impl fmt::Debug for Dir {

0 commit comments

Comments
 (0)