authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-17 18:25:50-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-12-17 18:25:50-05:00
log11b57470d08968a3aa3f3da522494df9450f8528
tree49c05a24199b5293135306515de0a715bdc9f883
parent90477e5c10c9c263a3c0038e1ae7b814d2c5397e
parent9e8ac2b6664b03116d7991d57ddb37c8108fffec
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13983 from squeek502/windows-childprocess-exec-retry

`ChildProcess.spawnWindows`: `PATH` search fixes + optimizations

2 files changed, 103 insertions(+), 44 deletions(-)

lib/std/child_process.zig+81-44
...@@ -965,56 +965,93 @@ pub const ChildProcess = struct {...@@ -965,56 +965,93 @@ pub const ChildProcess = struct {
965 const cmd_line_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, cmd_line);965 const cmd_line_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, cmd_line);
966 defer self.allocator.free(cmd_line_w);966 defer self.allocator.free(cmd_line_w);
967967
968 windowsCreateProcess(app_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {968 exec: {
969 if (no_path_err != error.FileNotFound) return no_path_err;969 windowsCreateProcess(app_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {
970970 switch (no_path_err) {
971 var free_path = true;971 error.FileNotFound, error.InvalidExe => {},
972 const PATH = process.getEnvVarOwned(self.allocator, "PATH") catch |err| switch (err) {972 else => |e| return e,
973 error.EnvironmentVariableNotFound => blk: {973 }
974 free_path = false;
975 break :blk "";
976 },
977 else => |e| return e,
978 };
979 defer if (free_path) self.allocator.free(PATH);
980
981 var free_path_ext = true;
982 const PATHEXT = process.getEnvVarOwned(self.allocator, "PATHEXT") catch |err| switch (err) {
983 error.EnvironmentVariableNotFound => blk: {
984 free_path_ext = false;
985 break :blk "";
986 },
987 else => |e| return e,
988 };
989 defer if (free_path_ext) self.allocator.free(PATHEXT);
990
991 const app_name = self.argv[0];
992
993 var it = mem.tokenize(u8, PATH, ";");
994 retry: while (it.next()) |search_path| {
995 const path_no_ext = try fs.path.join(self.allocator, &[_][]const u8{ search_path, app_name });
996 defer self.allocator.free(path_no_ext);
997
998 var ext_it = mem.tokenize(u8, PATHEXT, ";");
999 while (ext_it.next()) |app_ext| {
1000 const joined_path = try mem.concat(self.allocator, u8, &[_][]const u8{ path_no_ext, app_ext });
1001 defer self.allocator.free(joined_path);
1002974
1003 const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, joined_path);975 const PATH: [:0]const u16 = std.os.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse &[_:0]u16{};
1004 defer self.allocator.free(joined_path_w);976 const PATHEXT: [:0]const u16 = std.os.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATHEXT")) orelse &[_:0]u16{};
977
978 var path_buf = std.ArrayListUnmanaged(u16){};
979 defer path_buf.deinit(self.allocator);
980
981 // Try again with PATHEXT's extensions appended
982 {
983 try path_buf.appendSlice(self.allocator, app_path_w);
984 var ext_it = mem.tokenize(u16, PATHEXT, &[_]u16{';'});
985 while (ext_it.next()) |ext| {
986 path_buf.shrinkRetainingCapacity(app_path_w.len);
987 try path_buf.appendSlice(self.allocator, ext);
988 try path_buf.append(self.allocator, 0);
989 const path_with_ext = path_buf.items[0 .. path_buf.items.len - 1 :0];
990
991 if (windowsCreateProcess(path_with_ext.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
992 break :exec;
993 } else |err| switch (err) {
994 error.FileNotFound, error.AccessDenied, error.InvalidExe => {},
995 else => return err,
996 }
997 }
998 }
1005999
1006 if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {1000 // No need to search the PATH if the app path is absolute
1007 break :retry;1001 if (fs.path.isAbsoluteWindowsWTF16(app_path_w)) return no_path_err;
1002
1003 // app_path_w has the cwd prepended to it if cwd is non-null, so when
1004 // searching the PATH we should make sure we use the app_name verbatim.
1005 var app_name_w_needs_free = false;
1006 const app_name_w = x: {
1007 if (self.cwd) |_| {
1008 app_name_w_needs_free = true;
1009 break :x try unicode.utf8ToUtf16LeWithNull(self.allocator, self.argv[0]);
1010 } else {
1011 break :x app_path_w;
1012 }
1013 };
1014 defer if (app_name_w_needs_free) self.allocator.free(app_name_w);
1015
1016 var it = mem.tokenize(u16, PATH, &[_]u16{';'});
1017 while (it.next()) |search_path| {
1018 path_buf.clearRetainingCapacity();
1019 const search_path_trimmed = mem.trimRight(u16, search_path, &[_]u16{ '\\', '/' });
1020 try path_buf.appendSlice(self.allocator, search_path_trimmed);
1021 try path_buf.append(self.allocator, fs.path.sep);
1022 const app_name_trimmed = mem.trimLeft(u16, app_name_w, &[_]u16{ '\\', '/' });
1023 try path_buf.appendSlice(self.allocator, app_name_trimmed);
1024 try path_buf.append(self.allocator, 0);
1025 const path_no_ext = path_buf.items[0 .. path_buf.items.len - 1 :0];
1026
1027 if (windowsCreateProcess(path_no_ext.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
1028 break :exec;
1008 } else |err| switch (err) {1029 } else |err| switch (err) {
1009 error.FileNotFound => continue,1030 error.FileNotFound, error.AccessDenied, error.InvalidExe => {},
1010 error.AccessDenied => continue,
1011 else => return err,1031 else => return err,
1012 }1032 }
1033
1034 var ext_it = mem.tokenize(u16, PATHEXT, &[_]u16{';'});
1035 while (ext_it.next()) |ext| {
1036 path_buf.shrinkRetainingCapacity(path_no_ext.len);
1037 try path_buf.appendSlice(self.allocator, ext);
1038 try path_buf.append(self.allocator, 0);
1039 const joined_path = path_buf.items[0 .. path_buf.items.len - 1 :0];
1040
1041 if (windowsCreateProcess(joined_path.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
1042 break :exec;
1043 } else |err| switch (err) {
1044 error.FileNotFound => continue,
1045 error.AccessDenied => continue,
1046 error.InvalidExe => continue,
1047 else => return err,
1048 }
1049 }
1050 } else {
1051 return no_path_err; // return the original error
1013 }1052 }
1014 } else {1053 };
1015 return no_path_err; // return the original error1054 }
1016 }
1017 };
10181055
1019 if (g_hChildStd_IN_Wr) |h| {1056 if (g_hChildStd_IN_Wr) |h| {
1020 self.stdin = File{ .handle = h };1057 self.stdin = File{ .handle = h };
lib/std/os/windows.zig+22
...@@ -1569,6 +1569,7 @@ pub const CreateProcessError = error{...@@ -1569,6 +1569,7 @@ pub const CreateProcessError = error{
1569 AccessDenied,1569 AccessDenied,
1570 InvalidName,1570 InvalidName,
1571 NameTooLong,1571 NameTooLong,
1572 InvalidExe,
1572 Unexpected,1573 Unexpected,
1573};1574};
15741575
...@@ -1603,6 +1604,27 @@ pub fn CreateProcessW(...@@ -1603,6 +1604,27 @@ pub fn CreateProcessW(
1603 .INVALID_PARAMETER => unreachable,1604 .INVALID_PARAMETER => unreachable,
1604 .INVALID_NAME => return error.InvalidName,1605 .INVALID_NAME => return error.InvalidName,
1605 .FILENAME_EXCED_RANGE => return error.NameTooLong,1606 .FILENAME_EXCED_RANGE => return error.NameTooLong,
1607 // These are all the system errors that are mapped to ENOEXEC by
1608 // the undocumented _dosmaperr (old CRT) or __acrt_errno_map_os_error
1609 // (newer CRT) functions. Their code can be found in crt/src/dosmap.c (old SDK)
1610 // or urt/misc/errno.cpp (newer SDK) in the Windows SDK.
1611 .BAD_FORMAT,
1612 .INVALID_STARTING_CODESEG, // MIN_EXEC_ERROR in errno.cpp
1613 .INVALID_STACKSEG,
1614 .INVALID_MODULETYPE,
1615 .INVALID_EXE_SIGNATURE,
1616 .EXE_MARKED_INVALID,
1617 .BAD_EXE_FORMAT,
1618 .ITERATED_DATA_EXCEEDS_64k,
1619 .INVALID_MINALLOCSIZE,
1620 .DYNLINK_FROM_INVALID_RING,
1621 .IOPL_NOT_ENABLED,
1622 .INVALID_SEGDPL,
1623 .AUTODATASEG_EXCEEDS_64k,
1624 .RING2SEG_MUST_BE_MOVABLE,
1625 .RELOC_CHAIN_XEEDS_SEGLIM,
1626 .INFLOOP_IN_RELOC_CHAIN, // MAX_EXEC_ERROR in errno.cpp
1627 => return error.InvalidExe,
1606 else => |err| return unexpectedError(err),1628 else => |err| return unexpectedError(err),
1607 }1629 }
1608 }1630 }