@@ -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+ }
0 commit comments