diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index e6280b45604f31008c92ba01341808489327e2b0..932e401758b299bed777814bb1c806445da48774 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -1912,6 +1912,7 @@ pub const CreateProcessError = error{ NameTooLong, InvalidExe, SystemResources, + FileBusy, Unexpected, }; @@ -1982,6 +1983,7 @@ pub fn CreateProcessW( .INVALID_PARAMETER => unreachable, .INVALID_NAME => return error.InvalidName, .FILENAME_EXCED_RANGE => return error.NameTooLong, + .SHARING_VIOLATION => return error.FileBusy, // These are all the system errors that are mapped to ENOEXEC by // the undocumented _dosmaperr (old CRT) or __acrt_errno_map_os_error // (newer CRT) functions. Their code can be found in crt/src/dosmap.c (old SDK) diff --git a/src/link.zig b/src/link.zig index 1cf54f531c25cfd6810555d6e377f90427c27e52..9edd58109c3b90aa413b925508ff16dfe714d1d8 100644 --- a/src/link.zig +++ b/src/link.zig @@ -616,9 +616,19 @@ pub const File = struct { &coff.mf else unreachable; - mf.file = try base.emit.root_dir.handle.openFile(base.emit.sub_path, .{ + mf.file = for (0..2) |_| break base.emit.root_dir.handle.openFile(base.emit.sub_path, .{ .mode = .read_write, - }); + }) catch |err| switch (err) { + error.AccessDenied => switch (builtin.os.tag) { + .windows => { + // give the kernel a chance to finish closing the executable handle + std.os.windows.kernel32.Sleep(0); + continue; + }, + else => return error.AccessDenied, + }, + else => |e| return e, + } else return error.AccessDenied; base.file = mf.file; try mf.ensureTotalCapacity(@intCast(mf.nodes.items[0].location().resolve(mf)[1])); }, diff --git a/test/standalone/windows_spawn/main.zig b/test/standalone/windows_spawn/main.zig index 4cacf14c7c9e0fe0f0a1c24426742e06ce900fc7..661c32aa5c0b0f7a34c5155e01cfb5d43d44390f 100644 --- a/test/standalone/windows_spawn/main.zig +++ b/test/standalone/windows_spawn/main.zig @@ -71,7 +71,14 @@ pub fn main() anyerror!void { try testExec(allocator, "heLLo", "hello from exe\n"); // now rename the exe to not have an extension - try tmp.dir.rename("hello.exe", "hello"); + for (0..2) |_| break tmp.dir.rename("hello.exe", "hello") catch |err| switch (err) { + error.AccessDenied => { + // give the kernel a chance to finish closing the executable handle + std.os.windows.kernel32.Sleep(0); + continue; + }, + else => |e| return e, + } else return error.AccessDenied; // with extension should now fail try testExecError(error.FileNotFound, allocator, "hello.exe");