Skip to content
Draft
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
2 changes: 2 additions & 0 deletions crates/host_env/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ pub mod posix;
#[cfg(windows)]
#[path = "posix_windows.rs"]
pub mod posix;
#[cfg(any(unix, target_os = "wasi"))]
pub mod posix_unix_wasi;
#[cfg(unix)]
pub mod pwd;
#[cfg(unix)]
Expand Down
37 changes: 0 additions & 37 deletions crates/host_env/src/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ use core::ffi::CStr;
use core::str::Utf8Error;
#[cfg(windows)]
use core::time::Duration;
#[cfg(unix)]
use rustix::fd::AsFd;
use std::{
env,
ffi::{OsStr, OsString},
Expand Down Expand Up @@ -267,41 +265,6 @@ pub fn copy_file_range(
rustix::fs::copy_file_range(src, offset_src, dst, offset_dst, count)
}

#[cfg(not(unix))]
pub fn rename(
from: impl AsRef<std::path::Path>,
from_fd: Option<crt_fd::Borrowed<'_>>,
to: impl AsRef<std::path::Path>,
to_fd: Option<crt_fd::Borrowed<'_>>,
) -> io::Result<()> {
if from_fd.is_none() && to_fd.is_none() {
// TODO: Rust's implementation always overwrites the file so ensure consistency between
// operating systems. We need to use windows-sys directly to distinguish between
// os.rename and os.replace.
std::fs::rename(from, to)
} else {
core::hint::cold_path();
Err(io::Error::new(
io::ErrorKind::Unsupported,
"renameat is not available on this platform",
))
}
}

#[cfg(unix)]
pub fn rename(
from: impl AsRef<std::path::Path>,
from_fd: Option<crt_fd::Borrowed<'_>>,
to: impl AsRef<std::path::Path>,
to_fd: Option<crt_fd::Borrowed<'_>>,
) -> io::Result<()> {
let from = from.as_ref();
let from_fd = from_fd.as_ref().map_or(rustix::fs::CWD, AsFd::as_fd);
let to = to.as_ref();
let to_fd = to_fd.as_ref().map_or(rustix::fs::CWD, AsFd::as_fd);
rustix::fs::renameat(from_fd, from, to_fd, to).map_err(Into::into)
}

#[cfg(windows)]
pub fn seek_fd(
fd: crt_fd::Borrowed<'_>,
Expand Down
13 changes: 2 additions & 11 deletions crates/host_env/src/posix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use std::os::fd::FromRawFd;
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, IntoRawFd, OwnedFd};
use std::path::Path;

use crate::crt_fd;
pub use super::posix_unix_wasi::*;
pub use rustix::fs::RawMode;

pub struct UnameInfo {
pub sysname: String,
Expand Down Expand Up @@ -176,16 +177,6 @@ pub fn fcopyfile(in_fd: i32, out_fd: i32, flags: u32) -> std::io::Result<()> {
}
}

#[cfg(any(unix, target_os = "wasi"))]
pub fn make_dir(
dir_fd: Option<crt_fd::Borrowed<'_>>,
path: &impl AsRef<Path>,
mode: libc::mode_t,
) -> std::io::Result<()> {
let dir_fd = dir_fd.as_ref().map_or(rustix::fs::CWD, AsFd::as_fd);
rustix::fs::mkdirat(dir_fd, path.as_ref(), mode.into()).map_err(Into::into)
}

#[cfg(unix)]
pub fn link_paths(src: &CStr, dst: &CStr, follow_symlinks: bool) -> std::io::Result<()> {
let flags = if follow_symlinks {
Expand Down
45 changes: 45 additions & 0 deletions crates/host_env/src/posix_unix_wasi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//! Common POSIX implementations across Unix and WASI.

use std::{io, path::Path};

use rustix::{fd::AsFd, fs};

use crate::crt_fd;

/// https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdir.html
pub fn make_dir(
dir_fd: Option<crt_fd::Borrowed<'_>>,
path: &impl AsRef<Path>,
mode: fs::RawMode,
) -> io::Result<()> {
let dir_fd = dir_fd.as_ref().map_or(fs::CWD, AsFd::as_fd);
fs::mkdirat(dir_fd, path.as_ref(), mode.into()).map_err(Into::into)
}

/// https://pubs.opengroup.org/onlinepubs/9799919799/functions/rename.html
pub fn rename(
from: impl AsRef<Path>,
from_fd: Option<crt_fd::Borrowed<'_>>,
to: impl AsRef<Path>,
to_fd: Option<crt_fd::Borrowed<'_>>,
) -> io::Result<()> {
let from = from.as_ref();
let from_fd = from_fd.as_ref().map_or(fs::CWD, AsFd::as_fd);
let to = to.as_ref();
let to_fd = to_fd.as_ref().map_or(fs::CWD, AsFd::as_fd);
fs::renameat(from_fd, from, to_fd, to).map_err(Into::into)
}

/// https://docs.python.org/3/library/os.html#os.replace
///
/// Atomically replace `to` with `from`.
/// POSIX's rename already atomically replaces targets, so this function just forwards to [`rename`].
#[inline]
pub fn replace(
from: impl AsRef<Path>,
from_fd: Option<crt_fd::Borrowed<'_>>,
to: impl AsRef<Path>,
to_fd: Option<crt_fd::Borrowed<'_>>,
) -> io::Result<()> {
rename(from, from_fd, to, to_fd)
}
11 changes: 2 additions & 9 deletions crates/host_env/src/posix_wasi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,9 @@ use core::{ffi::CStr, time::Duration};
use rustix::fd::AsFd;
use std::{ffi::OsStr, io, path::Path};

use crate::{crt_fd, os::CheckLibcResult};
pub use super::posix_unix_wasi::*;

pub fn make_dir(
dir_fd: Option<crt_fd::Borrowed<'_>>,
path: &impl AsRef<Path>,
mode: libc::mode_t,
) -> std::io::Result<()> {
let dir_fd = dir_fd.as_ref().map_or(rustix::fs::CWD, AsFd::as_fd);
rustix::fs::mkdirat(dir_fd, path.as_ref(), mode.into()).map_err(Into::into)
}
use crate::{crt_fd, os::CheckLibcResult};

pub fn remove_dir_at(dir_fd: i32, path: &CStr) -> io::Result<()> {
unsafe { libc::unlinkat(dir_fd, path.as_ptr(), libc::AT_REMOVEDIR) }.check_libc_neg()?;
Expand Down
83 changes: 80 additions & 3 deletions crates/host_env/src/posix_windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,96 @@
//! these syscalls, but they can be emulated with a mix of the Windows API and the Rust standard
//! library, the latter of which calls the former.

use core::hint::cold_path;
use std::{fs, io, path::Path};

use widestring::WideCString;
use windows_sys::Win32::{
Foundation::FALSE,
Storage::FileSystem::{MOVE_FILE_FLAGS, MOVEFILE_REPLACE_EXISTING, MoveFileExW},
};

use crate::crt_fd;

#[expect(non_camel_case_types)]
pub type mode_t = u32;
pub type RawMode = u32;

pub fn make_dir(
dir_fd: Option<crt_fd::Borrowed<'_>>,
path: &impl AsRef<Path>,
_mode: mode_t,
_mode: RawMode,
) -> io::Result<()> {
debug_assert!(dir_fd.is_none());
// TODO: On Windows, Python has an override if the mode is 0o700
fs::create_dir(path)
}

/// https://pubs.opengroup.org/onlinepubs/9799919799/functions/rename.html
#[inline]
pub fn rename(
from: impl AsRef<Path>,
#[cfg_attr(not(debug_assertions), expect(unused_variables))] from_fd: Option<
crt_fd::Borrowed<'_>,
>,
to: impl AsRef<Path>,
#[cfg_attr(not(debug_assertions), expect(unused_variables))] to_fd: Option<
crt_fd::Borrowed<'_>,
>,
) -> io::Result<()> {
debug_assert!(from_fd.is_none());
debug_assert!(to_fd.is_none());

rename_impl(from, to, 0)
}

/// https://docs.python.org/3/library/os.html#os.replace
///
/// Atomically replace `to` with `from`.
#[inline]
pub fn replace(
from: impl AsRef<Path>,
#[cfg_attr(not(debug_assertions), expect(unused_variables))] from_fd: Option<
crt_fd::Borrowed<'_>,
>,
to: impl AsRef<Path>,
#[cfg_attr(not(debug_assertions), expect(unused_variables))] to_fd: Option<
crt_fd::Borrowed<'_>,
>,
) -> io::Result<()> {
debug_assert!(from_fd.is_none());
debug_assert!(to_fd.is_none());

rename_impl(from, to, MOVEFILE_REPLACE_EXISTING)
}

fn rename_impl(
from: impl AsRef<Path>,
to: impl AsRef<Path>,
flags: MOVE_FILE_FLAGS,
) -> io::Result<()> {
let from = WideCString::from_os_str(from.as_ref())
.map_err(io::Error::other)?
.into_vec_with_nul();
let to = WideCString::from_os_str(to.as_ref())
.map_err(io::Error::other)?
.into_vec_with_nul();

// SAFETY:
// * from and to are NUL terminated wide strings
let success = unsafe {
// Rust's [`std::fs::rename`] is more complicated than CPython's. Rust attempts to use modern APIs
// where available, such as `FileRenameInfoEx`, which better map to POSIX. CPython simply
// calls MoveFileExW so we'll do that for parity. However, it may be better to use the new
// APIs and fall back if possible, especially if they're faster.
//
// Unlike POSIX's rename, MoveFileExW does not automatically move between volumes.
// This is expected behavior in CPython.
MoveFileExW(from.as_ptr(), to.as_ptr(), flags)
};

if success != FALSE {
Ok(())
} else {
cold_path();
Err(io::Error::last_os_error())
}
}
Loading
Loading