/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
runtime/subprocess_windows/src/process_stdio.rs
510 строк
14 KB
David Sherret
refactor: force a reason on clippy overrides (#32761)
16 мар 2026, 15:41
Не верифицирован
16 мар 2026, 15:41
39a07b2
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ // Ported partly from https://github.com/libuv/libuv/blob/b00c5d1a09c094020044e79e19f478a25b8e1431/src/win/process-stdio.c use std::ffi::c_int; use std::ptr::null_mut; use buffer::StdioBuffer; use windows_sys::Win32::Foundation::CloseHandle; use windows_sys::Win32::Foundation::DUPLICATE_SAME_ACCESS; use windows_sys::Win32::Foundation::DuplicateHandle; use windows_sys::Win32::Foundation::GetLastError; use windows_sys::Win32::Foundation::HANDLE; use windows_sys::Win32::Foundation::HANDLE_FLAG_INHERIT; use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE; use windows_sys::Win32::Foundation::SetHandleInformation; use windows_sys::Win32::Security::SECURITY_ATTRIBUTES; use windows_sys::Win32::Storage::FileSystem::CreateFileW; use windows_sys::Win32::Storage::FileSystem::FILE_GENERIC_READ; use windows_sys::Win32::Storage::FileSystem::FILE_GENERIC_WRITE; use windows_sys::Win32::Storage::FileSystem::FILE_READ_ATTRIBUTES; use windows_sys::Win32::Storage::FileSystem::FILE_SHARE_READ; use windows_sys::Win32::Storage::FileSystem::FILE_SHARE_WRITE; use windows_sys::Win32::Storage::FileSystem::FILE_TYPE_CHAR; use windows_sys::Win32::Storage::FileSystem::FILE_TYPE_DISK; use windows_sys::Win32::Storage::FileSystem::FILE_TYPE_PIPE; use windows_sys::Win32::Storage::FileSystem::FILE_TYPE_REMOTE; use windows_sys::Win32::Storage::FileSystem::FILE_TYPE_UNKNOWN; use windows_sys::Win32::Storage::FileSystem::GetFileType; use windows_sys::Win32::Storage::FileSystem::OPEN_EXISTING; use windows_sys::Win32::System::Console::GetStdHandle; use windows_sys::Win32::System::Console::STD_ERROR_HANDLE; use windows_sys::Win32::System::Console::STD_HANDLE; use windows_sys::Win32::System::Console::STD_INPUT_HANDLE; use windows_sys::Win32::System::Console::STD_OUTPUT_HANDLE; use windows_sys::Win32::System::Threading::GetCurrentProcess; use windows_sys::Win32::System::Threading::GetStartupInfoW; use windows_sys::Win32::System::Threading::STARTUPINFOW; use crate::process::SpawnOptions; const FOPEN: u8 = 0x01; // const FEOFLAG: u8 = 0x02; // const FCRLF: u8 = 0x04; const FPIPE: u8 = 0x08; // const FNOINHERIT: u8 = 0x10; // const FAPPEND: u8 = 0x20; const FDEV: u8 = 0x40; // const FTEXT: u8 = 0x80; const fn child_stdio_size(count: usize) -> usize { size_of::<c_int>() + size_of::<u8>() * count + size_of::<usize>() * count } unsafe fn child_stdio_count(buffer: *mut u8) -> usize { unsafe { buffer.cast::<std::ffi::c_uint>().read_unaligned() as usize } // unsafe { *buffer.cast::<std::ffi::c_uint>() as usize } } unsafe fn child_stdio_handle(buffer: *mut u8, fd: i32) -> HANDLE { unsafe { buffer.add( size_of::<c_int>() + child_stdio_count(buffer) + size_of::<HANDLE>() * (fd as usize), ) } .cast() } unsafe fn child_stdio_crt_flags(buffer: *mut u8, fd: i32) -> *mut u8 { unsafe { buffer.add(size_of::<c_int>() + fd as usize) }.cast() } #[allow(dead_code, reason = "kept for parity with libuv")] unsafe fn uv_stdio_verify(buffer: *mut u8, size: u16) -> bool { if buffer.is_null() { return false; } if (size as usize) < child_stdio_size(0) { return false; } let count = unsafe { child_stdio_count(buffer) }; if count > 256 { return false; } if (size as usize) < child_stdio_size(count) { return false; } true } fn uv_create_nul_handle(access: u32) -> Result<HANDLE, std::io::Error> { let sa = SECURITY_ATTRIBUTES { nLength: size_of::<SECURITY_ATTRIBUTES>() as u32, lpSecurityDescriptor: null_mut(), bInheritHandle: 1, }; let handle = unsafe { CreateFileW( windows_sys::w!("NUL"), access, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, null_mut(), ) }; if handle == INVALID_HANDLE_VALUE { return Err(std::io::Error::last_os_error()); } Ok(handle) } #[allow(dead_code, reason = "kept for parity with libuv")] unsafe fn uv_stdio_noinherit(buffer: *mut u8) { let count = unsafe { child_stdio_count(buffer) }; for i in 0..count { let handle = unsafe { uv_stdio_handle(buffer, i as i32) }; if handle != INVALID_HANDLE_VALUE { unsafe { SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0) }; } } } pub(crate) unsafe fn uv_stdio_handle(buffer: *mut u8, fd: i32) -> HANDLE { let mut handle = INVALID_HANDLE_VALUE; unsafe { copy_handle( child_stdio_handle(buffer, fd) .cast::<HANDLE>() .read_unaligned(), &mut handle, ) }; handle } pub unsafe fn uv_duplicate_handle( handle: HANDLE, ) -> Result<HANDLE, std::io::Error> { if handle == INVALID_HANDLE_VALUE || handle.is_null() || handle == ((-2i32) as usize as HANDLE) { return Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, "Invalid handle", )); } let mut dup = INVALID_HANDLE_VALUE; let current_process = unsafe { GetCurrentProcess() }; if unsafe { DuplicateHandle( current_process, handle, current_process, &mut dup, 0, 1, DUPLICATE_SAME_ACCESS, ) } == 0 { return Err(std::io::Error::last_os_error()); } Ok(dup) } pub unsafe fn free_stdio_buffer(buffer: *mut u8) { let _ = unsafe { StdioBuffer::from_raw(buffer) }; } /*INLINE static HANDLE uv__get_osfhandle(int fd) { /* _get_osfhandle() raises an assert in debug builds if the FD is invalid. * But it also correctly checks the FD and returns INVALID_HANDLE_VALUE for * invalid FDs in release builds (or if you let the assert continue). So this * wrapper function disables asserts when calling _get_osfhandle. */ HANDLE handle; UV_BEGIN_DISABLE_CRT_ASSERT(); handle = (HANDLE) _get_osfhandle(fd); UV_END_DISABLE_CRT_ASSERT(); return handle; } */ unsafe fn uv_get_osfhandle(fd: i32) -> HANDLE { unsafe { libc::get_osfhandle(fd) as usize as HANDLE } } fn uv_duplicate_fd(fd: i32) -> Result<HANDLE, std::io::Error> { let handle = unsafe { uv_get_osfhandle(fd) }; unsafe { uv_duplicate_handle(handle) } } unsafe fn copy_handle(mut handle: HANDLE, dest: *mut HANDLE) { let handle = &raw mut handle; unsafe { std::ptr::copy_nonoverlapping( handle.cast::<u8>(), dest.cast::<u8>(), size_of::<HANDLE>(), ) } } #[derive(Debug, Clone, Copy)] pub enum StdioContainer { Ignore, InheritFd(i32), RawHandle(HANDLE), } #[inline(never)] pub(crate) fn uv_stdio_create( options: &SpawnOptions, ) -> Result<StdioBuffer, std::io::Error> { let mut count = options.stdio.len(); if count > 255 { return Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, "Invalid stdio count", )); } else if count < 3 { count = 3; } let mut buffer = StdioBuffer::new(count); for i in 0..count { let fdopt = if i < options.stdio.len() { options.stdio[i] } else { StdioContainer::Ignore }; match fdopt { StdioContainer::RawHandle(handle) => { let dup = unsafe { uv_duplicate_handle(handle)? }; unsafe { buffer.set_handle(i as i32, dup) }; let flags = unsafe { handle_file_type_flags(dup)? }; unsafe { buffer.set_flags(i as i32, flags) }; unsafe { CloseHandle(handle) }; } StdioContainer::Ignore => unsafe { if i <= 2 { let access = if i == 0 { FILE_GENERIC_READ } else { FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES }; let nul = uv_create_nul_handle(access)?; buffer.set_handle(i as i32, nul); buffer.set_flags(i as i32, FOPEN | FDEV); } }, StdioContainer::InheritFd(fd) => { let handle = uv_duplicate_fd(fd); let handle = match handle { Ok(handle) => handle, Err(_) if fd <= 2 => { unsafe { buffer.set_flags(fd, 0) }; unsafe { buffer.set_handle(fd, INVALID_HANDLE_VALUE) }; continue; } Err(e) => return Err(e), }; let flags = unsafe { handle_file_type_flags(handle)? }; unsafe { buffer.set_handle(fd, handle) }; unsafe { buffer.set_flags(fd, flags) }; } } } Ok(buffer) } unsafe fn handle_file_type_flags(handle: HANDLE) -> Result<u8, std::io::Error> { Ok(match unsafe { GetFileType(handle) } { FILE_TYPE_DISK => FOPEN, FILE_TYPE_PIPE => FOPEN | FPIPE, FILE_TYPE_CHAR | FILE_TYPE_REMOTE => FOPEN | FDEV, FILE_TYPE_UNKNOWN => { if unsafe { GetLastError() } != 0 { unsafe { CloseHandle(handle) }; return Err(std::io::Error::other("Unknown file type")); } FOPEN | FDEV } other => panic!("Unknown file type: {}", other), }) } pub fn disable_stdio_inheritance() { let no_inherit = |h: STD_HANDLE| unsafe { let handle = GetStdHandle(h); if !handle.is_null() && handle != INVALID_HANDLE_VALUE { SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0); } }; no_inherit(STD_INPUT_HANDLE); no_inherit(STD_OUTPUT_HANDLE); no_inherit(STD_ERROR_HANDLE); let mut si = unsafe { std::mem::zeroed::<STARTUPINFOW>() }; unsafe { GetStartupInfoW(&mut si) }; if let Some(mut stdio_buffer) = unsafe { StdioBuffer::from_raw_borrowed(si.lpReserved2, si.cbReserved2 as usize) } { stdio_buffer.no_inherit(); } } mod buffer { use std::ffi::c_uint; use std::mem::ManuallyDrop; use super::*; pub struct StdioBuffer { ptr: *mut u8, borrowed: bool, } impl Drop for StdioBuffer { fn drop(&mut self) { if self.borrowed { return; } let count = self.get_count(); for i in 0..count { let handle = unsafe { self.get_handle(i as i32) }; if handle != INVALID_HANDLE_VALUE { unsafe { CloseHandle(handle) }; } } unsafe { std::ptr::drop_in_place(self.ptr); std::alloc::dealloc( self.ptr as *mut _, std::alloc::Layout::array::<u8>(self.get_count()).unwrap(), ); } } } unsafe fn verify_buffer(ptr: *mut u8, size: usize) -> bool { if ptr.is_null() { return false; } if size < child_stdio_size(0) { return false; } let count = unsafe { child_stdio_count(ptr) }; if count > 256 { return false; } if size < child_stdio_size(count) { return false; } true } impl StdioBuffer { /// # Safety /// The buffer pointer must be valid and point to memory allocated by /// `std::alloc::alloc`. pub unsafe fn from_raw(ptr: *mut u8) -> Self { Self { ptr, borrowed: false, } } pub unsafe fn from_raw_borrowed(ptr: *mut u8, size: usize) -> Option<Self> { if unsafe { !verify_buffer(ptr, size) } { return None; } Some(Self { ptr, borrowed: true, }) } pub fn into_raw(self) -> *mut u8 { ManuallyDrop::new(self).ptr } fn create_raw(count: usize) -> Self { let layout = std::alloc::Layout::array::<u8>(child_stdio_size(count)).unwrap(); let ptr = unsafe { std::alloc::alloc(layout) }; StdioBuffer { ptr, borrowed: false, } } pub fn new(count: usize) -> Self { let buffer = Self::create_raw(count); // SAFETY: Since the buffer is uninitialized, use raw pointers // and do not read the data. unsafe { std::ptr::write(buffer.ptr.cast::<c_uint>(), count as c_uint); } for i in 0..count { // SAFETY: We initialized a big enough buffer for `count` // handles, so `i` is within bounds. unsafe { copy_handle( INVALID_HANDLE_VALUE, child_stdio_handle(buffer.ptr, i as i32).cast(), ); std::ptr::write(child_stdio_crt_flags(buffer.ptr, i as i32), 0); } } buffer } pub fn get_count(&self) -> usize { unsafe { child_stdio_count(self.ptr) } } /// # Safety /// /// This function does not check that the fd is within the bounds /// of the buffer. pub unsafe fn get_handle(&self, fd: i32) -> HANDLE { unsafe { uv_stdio_handle(self.ptr, fd) } } /// # Safety /// /// This function does not check that the fd is within the bounds /// of the buffer. pub unsafe fn set_flags(&mut self, fd: i32, flags: u8) { debug_assert!(fd < unsafe { child_stdio_count(self.ptr) } as i32,); unsafe { *child_stdio_crt_flags(self.ptr, fd) = flags; } } /// # Safety /// /// This function does not check that the fd is within the bounds /// of the buffer. pub unsafe fn set_handle(&mut self, fd: i32, handle: HANDLE) { unsafe { copy_handle(handle, child_stdio_handle(self.ptr, fd).cast()); } } pub fn size(&self) -> usize { child_stdio_size(self.get_count()) } pub fn no_inherit(&mut self) { let count = self.get_count(); for i in 0..count { let handle = unsafe { self.get_handle(i as i32) }; if handle != INVALID_HANDLE_VALUE { unsafe { SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0) }; } } } } }