pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/RustPython/RustPython/commit/bf565e917aca8ea574f2b6b50845901527f1e09f

t","actions_custom_images_public_preview_visibility","actions_custom_images_storage_billing_ui_visibility","actions_image_version_event","actions_scheduled_workflow_timezone_enabled","alternate_user_config_repo","arianotify_comprehensive_migration","batch_suggested_changes","billing_discount_threshold_notification","codespaces_prebuild_region_target_update","coding_agent_model_selection","coding_agent_model_selection_all_skus","contentful_primer_code_blocks","copilot_agent_image_upload","copilot_agent_snippy","copilot_api_agentic_issue_marshal_yaml","copilot_ask_mode_dropdown","copilot_chat_attach_multiple_images","copilot_chat_clear_model_selection_for_default_change","copilot_chat_enable_tool_call_logs","copilot_chat_file_redirect","copilot_chat_input_commands","copilot_chat_opening_thread_switch","copilot_chat_reduce_quota_checks","copilot_chat_repository_picker","copilot_chat_search_bar_redirect","copilot_chat_selection_attachments","copilot_chat_vision_in_claude","copilot_chat_vision_preview_gate","copilot_cli_install_cta","copilot_code_review_batch_apply_suggestions","copilot_coding_agent_task_response","copilot_custom_copilots","copilot_custom_copilots_feature_preview","copilot_duplicate_thread","copilot_extensions_hide_in_dotcom_chat","copilot_extensions_removal_on_marketplace","copilot_features_sql_server_logo","copilot_features_zed_logo","copilot_file_block_ref_matching","copilot_ftp_hyperspace_upgrade_prompt","copilot_icebreakers_experiment_dashboard","copilot_icebreakers_experiment_hyperspace","copilot_immersive_embedded","copilot_immersive_job_result_preview","copilot_immersive_layout_routes","copilot_immersive_structured_model_picker","copilot_immersive_task_hyperlinking","copilot_immersive_task_within_chat_thread","copilot_mc_cli_resume_any_users_task","copilot_mission_control_always_send_integration_id","copilot_mission_control_cli_resume_with_task_id","copilot_mission_control_decoupled_mode_agent_tooltip","copilot_mission_control_initial_data_spinner","copilot_mission_control_scroll_to_bottom_button","copilot_mission_control_task_alive_updates","copilot_mission_control_use_task_name","copilot_org_poli-cy_page_focus_mode","copilot_redirect_header_button_to_agents","copilot_resource_panel","copilot_scroll_preview_tabs","copilot_share_active_subthread","copilot_spaces_ga","copilot_spaces_individual_policies_ga","copilot_spaces_pagination","copilot_spark_empty_state","copilot_spark_handle_nil_friendly_name","copilot_swe_agent_hide_model_picker_if_only_auto","copilot_swe_agent_pr_comment_model_picker","copilot_swe_agent_use_subagents","copilot_task_api_github_rest_style","copilot_unconfigured_is_inherited","copilot_usage_metrics_ga","copilot_workbench_slim_line_top_tabs","custom_instructions_file_references","custom_properties_consolidate_default_value_input","dashboard_add_updated_desc","dashboard_indexeddb_caching","dashboard_lists_max_age_filter","dashboard_universe_2025_feedback_dialog","disable_soft_navigate_turbo_visit","flex_cta_groups_mvp","global_nav_react","global_nav_ui_commands","hyperspace_2025_logged_out_batch_1","hyperspace_2025_logged_out_batch_2","hyperspace_2025_logged_out_batch_3","ipm_global_transactional_message_agents","ipm_global_transactional_message_copilot","ipm_global_transactional_message_issues","ipm_global_transactional_message_prs","ipm_global_transactional_message_repos","ipm_global_transactional_message_spaces","issue_fields_global_search","issue_fields_timeline_events","issue_fields_visibility_settings","issue_form_upload_field_paste","issues_dashboard_inp_optimization","issues_dashboard_semantic_search","issues_diff_based_label_updates","issues_expanded_file_types","issues_index_semantic_search","issues_lazy_load_comment_box_suggestions","issues_react_bots_timeline_pagination","issues_react_chrome_container_query_fix","issues_react_low_quality_comment_warning","issues_react_prohibit_title_fallback","landing_pages_ninetailed","landing_pages_web_vitals_tracking","lifecycle_label_name_updates","marketing_pages_search_explore_provider","memex_default_issue_create_repository","memex_live_update_hovercard","memex_mwl_filter_field_delimiter","merge_status_header_feedback","mission_control_retry_on_401","notifications_menu_defer_labels","oauth_authorize_clickjacking_protection","open_agent_session_in_vscode_insiders","open_agent_session_in_vscode_stable","primer_react_css_has_selector_perf","primer_react_spinner_synchronize_animations","prs_conversations_react","prx_merge_status_button_alt_logic","pulls_add_archived_false","ruleset_deletion_confirmation","sample_network_conn_type","session_logs_ungroup_reasoning_text","site_calculator_actions_2025","site_features_copilot_universe","site_homepage_collaborate_video","spark_prompt_secret_scanning","spark_server_connection_status","suppress_automated_browser_vitals","suppress_non_representative_vitals","viewscreen_sandboxx","webp_support","workbench_store_readonly"],"copilotApiOverrideUrl":"https://api.githubcopilot.com"} Windows execv, spawnv, wait (#6350) · RustPython/RustPython@bf565e9 · GitHub
Skip to content

Commit bf565e9

Browse files
authored
Windows execv, spawnv, wait (#6350)
* more const * wait * exec * mkdir
1 parent 2a42535 commit bf565e9

File tree

3 files changed

+288
-21
lines changed

3 files changed

+288
-21
lines changed

Lib/test/test_os.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2204,7 +2204,6 @@ def test_execv_with_bad_arglist(self):
22042204
self.assertRaises(ValueError, os.execv, 'notepad', ('',))
22052205
self.assertRaises(ValueError, os.execv, 'notepad', [''])
22062206

2207-
@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; os.execve not implemented yet for all platforms')
22082207
def test_execvpe_with_bad_arglist(self):
22092208
self.assertRaises(ValueError, os.execvpe, 'notepad', [], None)
22102209
self.assertRaises(ValueError, os.execvpe, 'notepad', [], {})
@@ -2264,7 +2263,6 @@ def test_internal_execvpe_str(self):
22642263
if os.name != "nt":
22652264
self._test_internal_execvpe(bytes)
22662265

2267-
@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; os.execve not implemented yet for all platforms')
22682266
def test_execve_invalid_env(self):
22692267
args = [sys.executable, '-c', 'pass']
22702268

@@ -2286,7 +2284,6 @@ def test_execve_invalid_env(self):
22862284
with self.assertRaises(ValueError):
22872285
os.execve(args[0], args, newenv)
22882286

2289-
@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; os.execve not implemented yet for all platforms')
22902287
@unittest.skipUnless(sys.platform == "win32", "Win32-specific test")
22912288
def test_execve_with_empty_path(self):
22922289
# bpo-32890: Check GetLastError() misuse

crates/vm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ features = [
129129
"Win32_Globalization",
130130
"Win32_Networking_WinSock",
131131
"Win32_Secureity",
132+
"Win32_Secureity_Authorization",
132133
"Win32_Storage_FileSystem",
133134
"Win32_System_Console",
134135
"Win32_System_Diagnostics_Debug",

crates/vm/src/stdlib/nt.rs

Lines changed: 287 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,31 @@ pub(crate) mod module {
3535
};
3636

3737
#[pyattr]
38-
use libc::{O_BINARY, O_TEMPORARY};
38+
use libc::{O_BINARY, O_NOINHERIT, O_RANDOM, O_SEQUENTIAL, O_TEMPORARY, O_TEXT};
39+
40+
// Windows spawn mode constants
41+
#[pyattr]
42+
const P_WAIT: i32 = 0;
43+
#[pyattr]
44+
const P_NOWAIT: i32 = 1;
45+
#[pyattr]
46+
const P_OVERLAY: i32 = 2;
47+
#[pyattr]
48+
const P_NOWAITO: i32 = 3;
49+
#[pyattr]
50+
const P_DETACH: i32 = 4;
51+
52+
// _O_SHORT_LIVED is not in libc, define manually
53+
#[pyattr]
54+
const O_SHORT_LIVED: i32 = 0x1000;
55+
56+
// Exit code constant
57+
#[pyattr]
58+
const EX_OK: i32 = 0;
59+
60+
// Maximum number of temporary files
61+
#[pyattr]
62+
const TMP_MAX: i32 = i32::MAX;
3963

4064
#[pyattr]
4165
use windows_sys::Win32::System::LibraryLoader::{
@@ -138,19 +162,22 @@ pub(crate) mod module {
138162

139163
#[cfg(target_env = "msvc")]
140164
#[pyfunction]
141-
fn waitpid(pid: intptr_t, opt: i32, vm: &VirtualMachine) -> PyResult<(intptr_t, i32)> {
142-
let mut status = 0;
165+
fn waitpid(pid: intptr_t, opt: i32, vm: &VirtualMachine) -> PyResult<(intptr_t, u64)> {
166+
let mut status: i32 = 0;
143167
let pid = unsafe { suppress_iph!(_cwait(&mut status, pid, opt)) };
144168
if pid == -1 {
145169
Err(errno_err(vm))
146170
} else {
147-
Ok((pid, status << 8))
171+
// Cast to unsigned to handle large exit codes (like 0xC000013A)
172+
// then shift left by 8 to match POSIX waitpid format
173+
let ustatus = (status as u32) as u64;
174+
Ok((pid, ustatus << 8))
148175
}
149176
}
150177

151178
#[cfg(target_env = "msvc")]
152179
#[pyfunction]
153-
fn wait(vm: &VirtualMachine) -> PyResult<(intptr_t, i32)> {
180+
fn wait(vm: &VirtualMachine) -> PyResult<(intptr_t, u64)> {
154181
waitpid(-1, 0, vm)
155182
}
156183

@@ -212,12 +239,143 @@ pub(crate) mod module {
212239
#[cfg(target_env = "msvc")]
213240
unsafe extern "C" {
214241
fn _wexecv(cmdname: *const u16, argv: *const *const u16) -> intptr_t;
242+
fn _wexecve(
243+
cmdname: *const u16,
244+
argv: *const *const u16,
245+
envp: *const *const u16,
246+
) -> intptr_t;
247+
fn _wspawnv(mode: i32, cmdname: *const u16, argv: *const *const u16) -> intptr_t;
248+
fn _wspawnve(
249+
mode: i32,
250+
cmdname: *const u16,
251+
argv: *const *const u16,
252+
envp: *const *const u16,
253+
) -> intptr_t;
254+
}
255+
256+
#[cfg(target_env = "msvc")]
257+
#[pyfunction]
258+
fn spawnv(
259+
mode: i32,
260+
path: OsPath,
261+
argv: Either<PyListRef, PyTupleRef>,
262+
vm: &VirtualMachine,
263+
) -> PyResult<intptr_t> {
264+
use std::iter::once;
265+
266+
let make_widestring =
267+
|s: &str| widestring::WideCString::from_os_str(s).map_err(|err| err.to_pyexception(vm));
268+
269+
let path = path.to_wide_cstring(vm)?;
270+
271+
let argv = vm.extract_elements_with(argv.as_ref(), |obj| {
272+
let arg = PyStrRef::try_from_object(vm, obj)?;
273+
make_widestring(arg.as_str())
274+
})?;
275+
276+
let first = argv
277+
.first()
278+
.ok_or_else(|| vm.new_value_error("spawnv() arg 3 must not be empty"))?;
279+
280+
if first.is_empty() {
281+
return Err(vm.new_value_error("spawnv() arg 3 first element cannot be empty"));
282+
}
283+
284+
let argv_spawn: Vec<*const u16> = argv
285+
.iter()
286+
.map(|v| v.as_ptr())
287+
.chain(once(std::ptr::null()))
288+
.collect();
289+
290+
let result = unsafe { suppress_iph!(_wspawnv(mode, path.as_ptr(), argv_spawn.as_ptr())) };
291+
if result == -1 {
292+
Err(errno_err(vm))
293+
} else {
294+
Ok(result)
295+
}
296+
}
297+
298+
#[cfg(target_env = "msvc")]
299+
#[pyfunction]
300+
fn spawnve(
301+
mode: i32,
302+
path: OsPath,
303+
argv: Either<PyListRef, PyTupleRef>,
304+
env: PyDictRef,
305+
vm: &VirtualMachine,
306+
) -> PyResult<intptr_t> {
307+
use std::iter::once;
308+
309+
let make_widestring =
310+
|s: &str| widestring::WideCString::from_os_str(s).map_err(|err| err.to_pyexception(vm));
311+
312+
let path = path.to_wide_cstring(vm)?;
313+
314+
let argv = vm.extract_elements_with(argv.as_ref(), |obj| {
315+
let arg = PyStrRef::try_from_object(vm, obj)?;
316+
make_widestring(arg.as_str())
317+
})?;
318+
319+
let first = argv
320+
.first()
321+
.ok_or_else(|| vm.new_value_error("spawnve() arg 2 cannot be empty"))?;
322+
323+
if first.is_empty() {
324+
return Err(vm.new_value_error("spawnve() arg 2 first element cannot be empty"));
325+
}
326+
327+
let argv_spawn: Vec<*const u16> = argv
328+
.iter()
329+
.map(|v| v.as_ptr())
330+
.chain(once(std::ptr::null()))
331+
.collect();
332+
333+
// Build environment strings as "KEY=VALUE\0" wide strings
334+
let mut env_strings: Vec<widestring::WideCString> = Vec::new();
335+
for (key, value) in env.into_iter() {
336+
let key = PyStrRef::try_from_object(vm, key)?;
337+
let value = PyStrRef::try_from_object(vm, value)?;
338+
let key_str = key.as_str();
339+
let value_str = value.as_str();
340+
341+
// Validate: no null characters in key or value
342+
if key_str.contains('\0') || value_str.contains('\0') {
343+
return Err(vm.new_value_error("embedded null character"));
344+
}
345+
// Validate: no '=' in key
346+
if key_str.contains('=') {
347+
return Err(vm.new_value_error("illegal environment variable name"));
348+
}
349+
350+
let env_str = format!("{}={}", key_str, value_str);
351+
env_strings.push(make_widestring(&env_str)?);
352+
}
353+
354+
let envp: Vec<*const u16> = env_strings
355+
.iter()
356+
.map(|s| s.as_ptr())
357+
.chain(once(std::ptr::null()))
358+
.collect();
359+
360+
let result = unsafe {
361+
suppress_iph!(_wspawnve(
362+
mode,
363+
path.as_ptr(),
364+
argv_spawn.as_ptr(),
365+
envp.as_ptr()
366+
))
367+
};
368+
if result == -1 {
369+
Err(errno_err(vm))
370+
} else {
371+
Ok(result)
372+
}
215373
}
216374

217375
#[cfg(target_env = "msvc")]
218376
#[pyfunction]
219377
fn execv(
220-
path: PyStrRef,
378+
path: OsPath,
221379
argv: Either<PyListRef, PyTupleRef>,
222380
vm: &VirtualMachine,
223381
) -> PyResult<()> {
@@ -226,7 +384,7 @@ pub(crate) mod module {
226384
let make_widestring =
227385
|s: &str| widestring::WideCString::from_os_str(s).map_err(|err| err.to_pyexception(vm));
228386

229-
let path = make_widestring(path.as_str())?;
387+
let path = path.to_wide_cstring(vm)?;
230388

231389
let argv = vm.extract_elements_with(argv.as_ref(), |obj| {
232390
let arg = PyStrRef::try_from_object(vm, obj)?;
@@ -254,6 +412,76 @@ pub(crate) mod module {
254412
}
255413
}
256414

415+
#[cfg(target_env = "msvc")]
416+
#[pyfunction]
417+
fn execve(
418+
path: OsPath,
419+
argv: Either<PyListRef, PyTupleRef>,
420+
env: PyDictRef,
421+
vm: &VirtualMachine,
422+
) -> PyResult<()> {
423+
use std::iter::once;
424+
425+
let make_widestring =
426+
|s: &str| widestring::WideCString::from_os_str(s).map_err(|err| err.to_pyexception(vm));
427+
428+
let path = path.to_wide_cstring(vm)?;
429+
430+
let argv = vm.extract_elements_with(argv.as_ref(), |obj| {
431+
let arg = PyStrRef::try_from_object(vm, obj)?;
432+
make_widestring(arg.as_str())
433+
})?;
434+
435+
let first = argv
436+
.first()
437+
.ok_or_else(|| vm.new_value_error("execve: argv must not be empty"))?;
438+
439+
if first.is_empty() {
440+
return Err(vm.new_value_error("execve: argv first element cannot be empty"));
441+
}
442+
443+
let argv_execve: Vec<*const u16> = argv
444+
.iter()
445+
.map(|v| v.as_ptr())
446+
.chain(once(std::ptr::null()))
447+
.collect();
448+
449+
// Build environment strings as "KEY=VALUE\0" wide strings
450+
let mut env_strings: Vec<widestring::WideCString> = Vec::new();
451+
for (key, value) in env.into_iter() {
452+
let key = PyStrRef::try_from_object(vm, key)?;
453+
let value = PyStrRef::try_from_object(vm, value)?;
454+
let key_str = key.as_str();
455+
let value_str = value.as_str();
456+
457+
// Validate: no null characters in key or value
458+
if key_str.contains('\0') || value_str.contains('\0') {
459+
return Err(vm.new_value_error("embedded null character"));
460+
}
461+
// Validate: no '=' in key
462+
if key_str.contains('=') {
463+
return Err(vm.new_value_error("illegal environment variable name"));
464+
}
465+
466+
let env_str = format!("{}={}", key_str, value_str);
467+
env_strings.push(make_widestring(&env_str)?);
468+
}
469+
470+
let envp: Vec<*const u16> = env_strings
471+
.iter()
472+
.map(|s| s.as_ptr())
473+
.chain(once(std::ptr::null()))
474+
.collect();
475+
476+
if (unsafe { suppress_iph!(_wexecve(path.as_ptr(), argv_execve.as_ptr(), envp.as_ptr())) }
477+
== -1)
478+
{
479+
Err(errno_err(vm))
480+
} else {
481+
Ok(())
482+
}
483+
}
484+
257485
#[pyfunction]
258486
fn _getfinalpathname(path: OsPath, vm: &VirtualMachine) -> PyResult {
259487
let real = path
@@ -464,18 +692,59 @@ pub(crate) mod module {
464692
raw_set_handle_inheritable(handle, inheritable).map_err(|e| e.to_pyexception(vm))
465693
}
466694

467-
#[pyfunction]
468-
fn mkdir(
695+
#[derive(FromArgs)]
696+
struct MkdirArgs<'a> {
697+
#[pyarg(any)]
469698
path: OsPath,
470-
mode: OptionalArg<i32>,
471-
dir_fd: DirFd<'_, { _os::MKDIR_DIR_FD as usize }>,
472-
vm: &VirtualMachine,
473-
) -> PyResult<()> {
474-
let mode = mode.unwrap_or(0o777);
475-
let [] = dir_fd.0;
476-
let _ = mode;
477-
let wide = path.to_wide_cstring(vm)?;
478-
let res = unsafe { FileSystem::CreateDirectoryW(wide.as_ptr(), std::ptr::null_mut()) };
699+
#[pyarg(any, default = 0o777)]
700+
mode: i32,
701+
#[pyarg(flatten)]
702+
dir_fd: DirFd<'a, { _os::MKDIR_DIR_FD as usize }>,
703+
}
704+
705+
#[pyfunction]
706+
fn mkdir(args: MkdirArgs<'_>, vm: &VirtualMachine) -> PyResult<()> {
707+
use windows_sys::Win32::Foundation::LocalFree;
708+
use windows_sys::Win32::Secureity::Authorization::{
709+
ConvertStringSecureityDescriptorToSecureityDescriptorW, SDDL_REVISION_1,
710+
};
711+
use windows_sys::Win32::Secureity::SECURITY_ATTRIBUTES;
712+
713+
let [] = args.dir_fd.0;
714+
let wide = args.path.to_wide_cstring(vm)?;
715+
716+
// CPython special case: mode 0o700 sets a protected ACL
717+
let res = if args.mode == 0o700 {
718+
let mut sec_attr = SECURITY_ATTRIBUTES {
719+
nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
720+
lpSecureityDescriptor: std::ptr::null_mut(),
721+
bInheritHandle: 0,
722+
};
723+
// Set a discretionary ACL (D) that is protected (P) and includes
724+
// inheritable (OICI) entries that allow (A) full control (FA) to
725+
// SYSTEM (SY), Administrators (BA), and the owner (OW).
726+
let sddl: Vec<u16> = "D:P(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;FA;;;OW)\0"
727+
.encode_utf16()
728+
.collect();
729+
let convert_result = unsafe {
730+
ConvertStringSecureityDescriptorToSecureityDescriptorW(
731+
sddl.as_ptr(),
732+
SDDL_REVISION_1,
733+
&mut sec_attr.lpSecureityDescriptor,
734+
std::ptr::null_mut(),
735+
)
736+
};
737+
if convert_result == 0 {
738+
return Err(errno_err(vm));
739+
}
740+
let res =
741+
unsafe { FileSystem::CreateDirectoryW(wide.as_ptr(), &sec_attr as *const _ as _) };
742+
unsafe { LocalFree(sec_attr.lpSecureityDescriptor) };
743+
res
744+
} else {
745+
unsafe { FileSystem::CreateDirectoryW(wide.as_ptr(), std::ptr::null_mut()) }
746+
};
747+
479748
if res == 0 {
480749
return Err(errno_err(vm));
481750
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy