Skip to content

dirfd: preliminary unix and windows implementations #139514

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
493 changes: 493 additions & 0 deletions library/std/src/fs.rs

Large diffs are not rendered by default.

93 changes: 92 additions & 1 deletion library/std/src/fs/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use rand::RngCore;

use super::Dir;
#[cfg(any(
windows,
target_os = "freebsd",
Expand All @@ -17,7 +18,7 @@ use crate::char::MAX_LEN_UTF8;
target_vendor = "apple",
))]
use crate::fs::TryLockError;
use crate::fs::{self, File, FileTimes, OpenOptions};
use crate::fs::{self, File, FileTimes, OpenOptions, create_dir};
use crate::io::prelude::*;
use crate::io::{BorrowedBuf, ErrorKind, SeekFrom};
use crate::mem::MaybeUninit;
Expand Down Expand Up @@ -2084,3 +2085,93 @@ fn test_rename_junction() {
// Junction links are always absolute so we just check the file name is correct.
assert_eq!(fs::read_link(&dest).unwrap().file_name(), Some(not_exist.as_os_str()));
}

#[test]
fn test_dir_smoke_test() {
let tmpdir = tmpdir();
check!(Dir::new(tmpdir.path()));
}

#[test]
fn test_dir_read_file() {
let tmpdir = tmpdir();
let mut f = check!(File::create(tmpdir.join("foo.txt")));
check!(f.write(b"bar"));
check!(f.flush());
drop(f);
let dir = check!(Dir::new(tmpdir.path()));
let mut f = check!(dir.open("foo.txt"));
let mut buf = [0u8; 3];
check!(f.read_exact(&mut buf));
assert_eq!(b"bar", &buf);
}

#[test]
fn test_dir_write_file() {
let tmpdir = tmpdir();
let dir = check!(Dir::new(tmpdir.path()));
let mut f = check!(dir.open_with("foo.txt", &OpenOptions::new().write(true).create(true)));
check!(f.write(b"bar"));
check!(f.flush());
drop(f);
let mut f = check!(File::open(tmpdir.join("foo.txt")));
let mut buf = [0u8; 3];
check!(f.read_exact(&mut buf));
assert_eq!(b"bar", &buf);
}

#[test]
fn test_dir_remove_file() {
let tmpdir = tmpdir();
let mut f = check!(File::create(tmpdir.join("foo.txt")));
check!(f.write(b"bar"));
check!(f.flush());
drop(f);
let dir = check!(Dir::new(tmpdir.path()));
check!(dir.remove_file("foo.txt"));
let result = File::open(tmpdir.join("foo.txt"));
#[cfg(all(unix, not(target_os = "vxworks")))]
error!(result, "No such file or directory");
#[cfg(target_os = "vxworks")]
error!(result, "no such file or directory");
#[cfg(windows)]
error!(result, 2); // ERROR_FILE_NOT_FOUND
}

#[test]
fn test_dir_remove_dir() {
let tmpdir = tmpdir();
check!(create_dir(tmpdir.join("foo")));
let dir = check!(Dir::new(tmpdir.path()));
check!(dir.remove_dir("foo"));
let result = Dir::new(tmpdir.join("foo"));
#[cfg(all(unix, not(target_os = "vxworks")))]
error!(result, "No such file or directory");
#[cfg(target_os = "vxworks")]
error!(result, "no such file or directory");
#[cfg(windows)]
error!(result, 2); // ERROR_FILE_NOT_FOUND
}

#[test]
fn test_dir_rename_file() {
let tmpdir = tmpdir();
let mut f = check!(File::create(tmpdir.join("foo.txt")));
check!(f.write(b"bar"));
check!(f.flush());
drop(f);
let dir = check!(Dir::new(tmpdir.path()));
check!(dir.rename("foo.txt", &dir, "baz.txt"));
let mut f = check!(File::open(tmpdir.join("baz.txt")));
let mut buf = [0u8; 3];
check!(f.read_exact(&mut buf));
assert_eq!(b"bar", &buf);
}

#[test]
fn test_dir_create_dir() {
let tmpdir = tmpdir();
let dir = check!(Dir::new(tmpdir.path()));
check!(dir.create_dir("foo"));
check!(Dir::new(tmpdir.join("foo")));
}
69 changes: 67 additions & 2 deletions library/std/src/sys/fs/common.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#![allow(dead_code)] // not used on all platforms

use crate::fs;
use crate::fmt;
use crate::fs::{self, create_dir, remove_dir, remove_file, rename};
use crate::io::{self, Error, ErrorKind};
use crate::path::Path;
use crate::path::{Path, PathBuf};
use crate::sys::fs::{File, OpenOptions};
use crate::sys_common::ignore_notfound;

pub(crate) const NOT_FILE_ERROR: Error = io::const_error!(
Expand Down Expand Up @@ -58,3 +60,66 @@ pub fn exists(path: &Path) -> io::Result<bool> {
Err(error) => Err(error),
}
}

pub struct Dir {
path: PathBuf,
}

impl Dir {
pub fn new<P: AsRef<Path>>(path: P) -> io::Result<Self> {
Ok(Self { path: path.as_ref().to_path_buf() })
}

pub fn new_with<P: AsRef<Path>>(path: P, _opts: &OpenOptions) -> io::Result<Self> {
Ok(Self { path: path.as_ref().to_path_buf() })
}

pub fn new_for_traversal<P: AsRef<Path>>(path: P) -> io::Result<Self> {
Ok(Self { path: path.as_ref().to_path_buf() })
}

pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
let mut opts = OpenOptions::new();
opts.read(true);
File::open(&self.path.join(path), &opts)
}

pub fn open_with<P: AsRef<Path>>(&self, path: P, opts: &OpenOptions) -> io::Result<File> {
File::open(&self.path.join(path), opts)
}

pub fn create_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
create_dir(self.path.join(path))
}

pub fn open_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<Self> {
Self::new(self.path.join(path))
}

pub fn open_dir_with<P: AsRef<Path>>(&self, path: P, opts: &OpenOptions) -> io::Result<Self> {
Self::new_with(self.path.join(path), opts)
}

pub fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
remove_file(self.path.join(path))
}

pub fn remove_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
remove_dir(self.path.join(path))
}

pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(
&self,
from: P,
to_dir: &Self,
to: Q,
) -> io::Result<()> {
rename(self.path.join(from), to_dir.path.join(to))
}
}

impl fmt::Debug for Dir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Dir").field("path", &self.path).finish()
}
}
30 changes: 29 additions & 1 deletion library/std/src/sys/fs/hermit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ use crate::path::{Path, PathBuf};
use crate::sync::Arc;
use crate::sys::common::small_c_string::run_path_with_cstr;
use crate::sys::fd::FileDesc;
pub use crate::sys::fs::common::{copy, exists};
pub use crate::sys::fs::common::{Dir, copy, exists};
use crate::sys::fs::{remove_dir, remove_file};
use crate::sys::time::SystemTime;
use crate::sys::{cvt, unsupported, unsupported_err};
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
use crate::{fmt, mem};

#[derive(Debug)]
pub struct File(FileDesc);

#[derive(Clone)]
pub struct FileAttr {
stat_val: stat_struct,
Expand Down Expand Up @@ -251,6 +253,32 @@ impl DirEntry {
pub fn file_name_os_str(&self) -> &OsStr {
self.name.as_os_str()
}

pub fn open_file(&self) -> io::Result<File> {
let mut opts = OpenOptions::new();
opts.read(true);
File::open(&self.path(), &opts)
}

pub fn open_file_with(&self, opts: &OpenOptions) -> io::Result<File> {
File::open(&self.path(), opts)
}

pub fn open_dir(&self) -> io::Result<Dir> {
Dir::new(self.path())
}

pub fn open_dir_with(&self, opts: &OpenOptions) -> io::Result<Dir> {
Dir::new_with(self.path(), opts)
}

pub fn remove_file(&self) -> io::Result<()> {
remove_file(&self.path())
}

pub fn remove_dir(&self) -> io::Result<()> {
remove_dir(&self.path())
}
}

impl OpenOptions {
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sys/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub fn with_native_path<T>(path: &Path, f: &dyn Fn(&Path) -> io::Result<T>) -> i
}

pub use imp::{
DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions,
Dir, DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions,
ReadDir,
};

Expand Down
29 changes: 28 additions & 1 deletion library/std/src/sys/fs/solid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use crate::os::raw::{c_int, c_short};
use crate::os::solid::ffi::OsStrExt;
use crate::path::{Path, PathBuf};
use crate::sync::Arc;
pub use crate::sys::fs::common::exists;
pub use crate::sys::fs::common::{Dir, exists};
use crate::sys::fs::{remove_dir, remove_file};
use crate::sys::pal::{abi, error};
use crate::sys::time::SystemTime;
use crate::sys::{unsupported, unsupported_err};
Expand Down Expand Up @@ -219,6 +220,32 @@ impl DirEntry {
_ => lstat(&self.path()).map(|m| m.file_type()),
}
}

pub fn open_file(&self) -> io::Result<File> {
let mut opts = OpenOptions::new();
opts.read(true);
File::open(&self.path(), &opts)
}

pub fn open_file_with(&self, opts: &OpenOptions) -> io::Result<File> {
File::open(&self.path(), opts)
}

pub fn open_dir(&self) -> io::Result<Dir> {
Dir::new(self.path())
}

pub fn open_dir_with(&self, opts: &OpenOptions) -> io::Result<Dir> {
Dir::new_with(self.path(), opts)
}

pub fn remove_file(&self) -> io::Result<()> {
remove_file(&self.path())
}

pub fn remove_dir(&self) -> io::Result<()> {
remove_dir(&self.path())
}
}

impl OpenOptions {
Expand Down
83 changes: 83 additions & 0 deletions library/std/src/sys/fs/uefi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub struct FileAttr {
size: u64,
}

pub struct Dir(!);

pub struct ReadDir(!);

pub struct DirEntry(!);
Expand Down Expand Up @@ -115,6 +117,63 @@ impl FileType {
}
}

impl Dir {
pub fn new<P: AsRef<Path>>(_path: P) -> io::Result<Self> {
unsupported()
}

pub fn new_with<P: AsRef<Path>>(_path: P, opts: &OpenOptions) -> io::Result<Self> {
unsupported()
}

pub fn new_for_traversal<P: AsRef<Path>>(_path: P) -> io::Result<Self> {
unsupported()
}

pub fn open<P: AsRef<Path>>(&self, _path: P) -> io::Result<File> {
self.0
}

pub fn open_with<P: AsRef<Path>>(&self, _path: P, opts: &OpenOptions) -> io::Result<File> {
self.0
}

pub fn create_dir<P: AsRef<Path>>(&self, _path: P) -> io::Result<()> {
self.0
}

pub fn open_dir<P: AsRef<Path>>(&self, _path: P) -> io::Result<Self> {
self.0
}

pub fn open_dir_with<P: AsRef<Path>>(&self, _path: P, _opts: &OpenOptions) -> io::Result<Self> {
self.0
}

pub fn remove_file<P: AsRef<Path>>(&self, _path: P) -> io::Result<()> {
self.0
}

pub fn remove_dir<P: AsRef<Path>>(&self, _path: P) -> io::Result<()> {
self.0
}

pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(
&self,
_from: P,
_to_dir: &Self,
_to: Q,
) -> io::Result<()> {
self.0
}
}

impl fmt::Debug for Dir {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0
}
}

impl fmt::Debug for ReadDir {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0
Expand Down Expand Up @@ -145,6 +204,30 @@ impl DirEntry {
pub fn file_type(&self) -> io::Result<FileType> {
self.0
}

pub fn open_file(&self) -> io::Result<File> {
self.0
}

pub fn open_file_with(&self, _opts: &OpenOptions) -> io::Result<File> {
self.0
}

pub fn open_dir(&self) -> io::Result<Dir> {
self.0
}

pub fn open_dir_with(&self, _opts: &OpenOptions) -> io::Result<Dir> {
self.0
}

pub fn remove_file(&self) -> io::Result<()> {
self.0
}

pub fn remove_dir(&self) -> io::Result<()> {
self.0
}
}

impl OpenOptions {
Expand Down
Loading
Loading