10BC0 fileutils::fopen · RustPython/RustPython@516df51 · GitHub
[go: up one dir, main page]

Skip to content

Commit 516df51

Browse files
committed
fileutils::fopen
1 parent 9510492 commit 516df51

File tree

4 files changed

+148
-28
lines changed

4 files changed

+148
-28
lines changed

Lib/test/test_httplib.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,8 +1801,6 @@ def test_local_good_hostname(self):
18011801
self.addCleanup(resp.close)
18021802
self.assertEqual(resp.status, 404)
18031803

1804-
# TODO: RUSTPYTHON
1805-
@unittest.expectedFailure
18061804
def test_local_bad_hostname(self):
18071805
# The (valid) cert doesn't validate the HTTP hostname
18081806
import ssl

Lib/test/test_urllib2_localnet.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -602,8 +602,6 @@ def test_https_with_cadefault(self):
602602
self.urlopen("https://localhost:%s/bizarre" % handler.port,
603603
cadefault=True)
604604

605-
# TODO: RUSTPYTHON
606-
@unittest.expectedFailure
607605
@unittest.skipIf(os.name == "nt", "TODO: RUSTPYTHON, ValueError: illegal environment variable name")
608606
def test_https_sni(self):
609607
if ssl is None:

common/src/fileutils.rs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,3 +438,140 @@ pub mod windows {
438438
}
439439
}
440440
}
441+
442+
// _Py_fopen_obj in cpython (Python/fileutils.c:1757-1835)
443+
// Open a file using libc::fopen (Unix) or libc::_wfopen (Windows)
444+
// and set the file descriptor to non-inheritable (close-on-exec)
445+
pub fn fopen(path: &std::path::Path, mode: &str) -> std::io::Result<*mut libc::FILE> {
446+
use std::ffi::CString;
447+
448+
#[cfg(windows)]
449+
{
450+
use crate::windows::ToWideString;
451+
use std::os::windows::ffi::OsStrExt;
452+
453+
// Convert path to wide string
454+
let wpath = path.as_os_str().to_wide_with_nul();
455+
456+
// Convert mode to wide string
457+
let wmode: Vec<u16> = mode.encode_utf16().chain(std::iter::once(0)).collect();
458+
459+
// Call _wfopen with retry on EINTR
460+
let mut f: *mut libc::FILE = std::ptr::null_mut();
461+
loop {
462+
f = unsafe { libc::_wfopen(wpath.as_ptr(), wmode.as_ptr()) };
463+
if !f.is_null() {
464+
break;
465+
}
466+
// Check if error is EINTR (interrupted)
467+
let err = std::io::Error::last_os_error();
468+
if err.kind() != std::io::ErrorKind::Interrupted {
469+
break;
470+
}
471+
}
472+
473+
if f.is_null() {
474+
return Err(std::io::Error::last_os_error());
475+
}
476+
477+
// Set non-inheritable
478+
let fd = unsafe { libc::fileno(f) };
479+
if let Err(e) = set_inheritable(fd, false) {
480+
unsafe { libc::fclose(f) };
481+
return Err(e);
482+
}
483+
484+
Ok(f)
485+
}
486+
487+
#[cfg(not(windows))]
488+
{
489+
// Convert path to CString
490+
let path_cstring = CString::new(path.as_os_str().as_encoded_bytes())
491+
.map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidInput, "invalid path"))?;
492+
let mode_cstring = CString::new(mode)
493+
.map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidInput, "invalid mode"))?;
494+
495+
// Call fopen with retry on EINTR
496+
let mut f: *mut libc::FILE;
497+
loop {
498+
f = unsafe { libc::fopen(path_cstring.as_ptr(), mode_cstring.as_ptr()) };
499+
if !f.is_null() {
500+
break;
501+
}
502+
// Check if error is EINTR (interrupted)
503+
let err = std::io::Error::last_os_error();
504+
if err.kind() != std::io::ErrorKind::Interrupted {
505+
break;
506+
}
507+
}
508+
509+
if f.is_null() {
510+
return Err(std::io::Error::last_os_error());
511+
}
512+
513+
// Set non-inheritable
514+
let fd = unsafe { libc::fileno(f) };
515+
if let Err(e) = set_inheritable(fd, false) {
516+
unsafe { libc::fclose(f) };
517+
return Err(e);
518+
}
519+
520+
Ok(f)
521+
}
522+
}
523+
524+
// set_inheritable in cpython (Python/fileutils.c:1443-1570)
525+
// Set the inheritable flag of the specified file descriptor
526+
fn set_inheritable(fd: libc::c_int, inheritable: bool) -> std::io::Result<()> {
527+
#[cfg(windows)]
528+
{
529+
use windows_sys::Win32::Foundation::SetHandleInformation;
530+
use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE};
531+
use windows_sys::Win32::System::Console::HANDLE_FLAG_INHERIT;
532+
533+
let handle = unsafe { libc::get_osfhandle(fd) };
534+
if handle == INVALID_HANDLE_VALUE as isize {
535+
return Err(std::io::Error::last_os_error());
536+
}
537+
538+
let flags = if inheritable { HANDLE_FLAG_INHERIT } else { 0 };
539+
let result = unsafe { SetHandleInformation(handle as HANDLE, HANDLE_FLAG_INHERIT, flags) };
540+
if result == 0 {
541+
return Err(std::io::Error::last_os_error());
542+
}
543+
544+
Ok(())
545+
}
546+
547+
#[cfg(not(windows))]
548+
{
549+
use libc::{F_GETFD, F_SETFD, FD_CLOEXEC};
550+
551+
// Get current flags
552+
let flags = unsafe { libc::fcntl(fd, F_GETFD) };
553+
if flags < 0 {
554+
return Err(std::io::Error::last_os_error());
555+
}
556+
557+
// Set or clear FD_CLOEXEC
558+
let new_flags = if inheritable {
559+
flags & !FD_CLOEXEC
560+
} else {
561+
flags | FD_CLOEXEC
562+
};
563+
564+
// If flags are already correct, return early
565+
if new_flags == flags {
566+
return Ok(());
567+
}
568+
569+
// Set new flags
570+
let result = unsafe { libc::fcntl(fd, F_SETFD, new_flags) };
571+
if result < 0 {
572+
return Err(std::io::Error::last_os_error());
573+
}
574+
575+
Ok(())
576+
}
577+
}

stdlib/src/ssl.rs

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,32 +1306,19 @@ mod _ssl {
13061306

13071307
#[pymethod]
13081308
fn load_dh_params(&self, filepath: FsPath, vm: &VirtualMachine) -> PyResult<()> {
1309-
use std::fs::File;
1310-
use std::os::unix::io::IntoRawFd;
1311-
13121309
let path = filepath.to_path_buf(vm)?;
13131310

1314-
// Open the file
1315-
let file = File::open(&path).map_err(|e| match e.kind() {
1316-
std::io::ErrorKind::NotFound => vm.new_exception_msg(
1317-
vm.ctx.exceptions.file_not_found_error.to_owned(),
1318-
e.to_string(),
1319-
),
1320-
_ => vm.new_os_error(e.to_string()),
1321-
})?;
1322-
1323-
// Get the raw file descriptor
1324-
let fd = file.into_raw_fd();
1325-
1326-
// Convert to FILE* using fdopen
1327-
let mode = std::ffi::CString::new("rb").unwrap();
1328-
let fp = unsafe { libc::fdopen(fd, mode.as_ptr()) };
1329-
if fp.is_null() {
1330-
unsafe {
1331-
libc::close(fd);
1332-
}
1333-
return Err(vm.new_os_error("failed to open file".to_owned()));
1334-
}
1311+
// Open the file using fopen (cross-platform)
1312+
let fp =
1313+
rustpython_common::fileutils::fopen(path.as_path(), "rb").map_err(|e| {
1314+
match e.kind() {
1315+
std::io::ErrorKind::NotFound => vm.new_exception_msg(
1316+
vm.ctx.exceptions.file_not_found_error.to_owned(),
1317+
e.to_string(),
1318+
),
1319+
_ => vm.new_os_error(e.to_string()),
1320+
}
1321+
})?;
13351322

13361323
// Read DH parameters
13371324
let dh = unsafe {

0 commit comments

Comments
 (0)
0