authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-27 10:07:29-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:51-07:00
log6ccb53bff130a473f9d2cc3d0c1ad3a8c6399b2d
treecae5cccd6175aacf5cab867b730d349265900280
parent441d0c4272e42b35951cc5e0bcfd2139f73edec8

std.Io.Threaded: fix openSelfExe for Windows

missing a call to wToPrefixedFileW

3 files changed, 101 insertions(+), 29 deletions(-)

lib/std/Io/Threaded.zig+96-25
...@@ -2044,26 +2044,97 @@ fn dirOpenFileWindows(...@@ -2044,26 +2044,97 @@ fn dirOpenFileWindows(
2044 const t: *Threaded = @ptrCast(@alignCast(userdata));2044 const t: *Threaded = @ptrCast(@alignCast(userdata));
2045 const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path);2045 const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
2046 const sub_path_w = sub_path_w_array.span();2046 const sub_path_w = sub_path_w_array.span();
2047 return dirOpenFileWindowsInner(t, dir, sub_path_w, flags);2047 const dir_handle = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle;
2048 return dirOpenFileWindowsInner(t, dir_handle, sub_path_w, flags);
2048}2049}
20492050
2050fn dirOpenFileWindowsInner(2051fn dirOpenFileWindowsInner(
2051 t: *Threaded,2052 t: *Threaded,
2052 dir: Io.Dir,2053 dir_handle: ?windows.HANDLE,
2053 sub_path_w: [:0]const u16,2054 sub_path_w: [:0]const u16,
2054 flags: Io.File.OpenFlags,2055 flags: Io.File.OpenFlags,
2055) Io.File.OpenError!Io.File {2056) Io.File.OpenError!Io.File {
2056 try t.checkCancel();2057 if (std.mem.eql(u16, sub_path_w, &.{'.'})) return error.IsDir;
2058 if (std.mem.eql(u16, sub_path_w, &.{ '.', '.' })) return error.IsDir;
2059 const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
2060
2057 const w = windows;2061 const w = windows;
2058 const handle = try w.OpenFile(sub_path_w, .{2062
2059 .dir = dir.handle,2063 var nt_name: w.UNICODE_STRING = .{
2060 .access_mask = w.SYNCHRONIZE |2064 .Length = path_len_bytes,
2061 (if (flags.isRead()) @as(u32, w.GENERIC_READ) else 0) |2065 .MaximumLength = path_len_bytes,
2062 (if (flags.isWrite()) @as(u32, w.GENERIC_WRITE) else 0),2066 .Buffer = @constCast(sub_path_w.ptr),
2063 .creation = w.FILE_OPEN,2067 };
2064 });2068 var attr: w.OBJECT_ATTRIBUTES = .{
2065 errdefer w.CloseHandle(handle);2069 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
2070 .RootDirectory = dir_handle,
2071 .Attributes = 0,
2072 .ObjectName = &nt_name,
2073 .SecurityDescriptor = null,
2074 .SecurityQualityOfService = null,
2075 };
2066 var io_status_block: w.IO_STATUS_BLOCK = undefined;2076 var io_status_block: w.IO_STATUS_BLOCK = undefined;
2077 const blocking_flag: w.ULONG = w.FILE_SYNCHRONOUS_IO_NONALERT;
2078 const file_or_dir_flag: w.ULONG = w.FILE_NON_DIRECTORY_FILE;
2079 // If we're not following symlinks, we need to ensure we don't pass in any
2080 // synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT.
2081 const create_file_flags: w.ULONG = file_or_dir_flag |
2082 if (flags.follow_symlinks) blocking_flag else w.FILE_OPEN_REPARSE_POINT;
2083
2084 const handle = while (true) {
2085 try t.checkCancel();
2086
2087 var result: w.HANDLE = undefined;
2088 const rc = w.ntdll.NtCreateFile(
2089 &result,
2090 w.SYNCHRONIZE |
2091 (if (flags.isRead()) @as(u32, w.GENERIC_READ) else 0) |
2092 (if (flags.isWrite()) @as(u32, w.GENERIC_WRITE) else 0),
2093 &attr,
2094 &io_status_block,
2095 null,
2096 w.FILE_ATTRIBUTE_NORMAL,
2097 w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE,
2098 w.FILE_OPEN,
2099 create_file_flags,
2100 null,
2101 0,
2102 );
2103 switch (rc) {
2104 .SUCCESS => break result,
2105 .OBJECT_NAME_INVALID => return error.BadPathName,
2106 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
2107 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
2108 .BAD_NETWORK_PATH => return error.NetworkNotFound, // \\server was not found
2109 .BAD_NETWORK_NAME => return error.NetworkNotFound, // \\server was found but \\server\share wasn't
2110 .NO_MEDIA_IN_DEVICE => return error.NoDevice,
2111 .INVALID_PARAMETER => |err| return w.statusBug(err),
2112 .SHARING_VIOLATION => return error.AccessDenied,
2113 .ACCESS_DENIED => return error.AccessDenied,
2114 .PIPE_BUSY => return error.PipeBusy,
2115 .PIPE_NOT_AVAILABLE => return error.NoDevice,
2116 .OBJECT_PATH_SYNTAX_BAD => |err| return w.statusBug(err),
2117 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
2118 .FILE_IS_A_DIRECTORY => return error.IsDir,
2119 .NOT_A_DIRECTORY => return error.NotDir,
2120 .USER_MAPPED_FILE => return error.AccessDenied,
2121 .INVALID_HANDLE => |err| return w.statusBug(err),
2122 .DELETE_PENDING => {
2123 // This error means that there *was* a file in this location on
2124 // the file system, but it was deleted. However, the OS is not
2125 // finished with the deletion operation, and so this CreateFile
2126 // call has failed. There is not really a sane way to handle
2127 // this other than retrying the creation after the OS finishes
2128 // the deletion.
2129 _ = w.kernel32.SleepEx(1, w.FALSE);
2130 continue;
2131 },
2132 .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference,
2133 else => return w.unexpectedStatus(rc),
2134 }
2135 };
2136 errdefer w.CloseHandle(handle);
2137
2067 const range_off: w.LARGE_INTEGER = 0;2138 const range_off: w.LARGE_INTEGER = 0;
2068 const range_len: w.LARGE_INTEGER = 1;2139 const range_len: w.LARGE_INTEGER = 1;
2069 const exclusive = switch (flags.lock) {2140 const exclusive = switch (flags.lock) {
...@@ -2691,20 +2762,19 @@ fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekErr...@@ -2691,20 +2762,19 @@ fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekErr
26912762
2692fn openSelfExe(userdata: ?*anyopaque, flags: Io.File.OpenFlags) Io.File.OpenSelfExeError!Io.File {2763fn openSelfExe(userdata: ?*anyopaque, flags: Io.File.OpenFlags) Io.File.OpenSelfExeError!Io.File {
2693 const t: *Threaded = @ptrCast(@alignCast(userdata));2764 const t: *Threaded = @ptrCast(@alignCast(userdata));
2694 if (native_os == .linux or native_os == .serenity) {2765 switch (native_os) {
2695 return dirOpenFilePosix(t, .{ .handle = posix.AT.FDCWD }, "/proc/self/exe", flags);2766 .linux, .serenity => return dirOpenFilePosix(t, .{ .handle = posix.AT.FDCWD }, "/proc/self/exe", flags),
2696 }2767 .windows => {
2697 if (is_windows) {2768 // If ImagePathName is a symlink, then it will contain the path of the symlink,
2698 // If ImagePathName is a symlink, then it will contain the path of the symlink,2769 // not the path that the symlink points to. However, because we are opening
2699 // not the path that the symlink points to. However, because we are opening2770 // the file, we can let the openFileW call follow the symlink for us.
2700 // the file, we can let the openFileW call follow the symlink for us.2771 const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
2701 const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;2772 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
2702 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];2773 const prefixed_path_w = try windows.wToPrefixedFileW(null, image_path_name);
2703 const cwd_handle = std.os.windows.peb().ProcessParameters.CurrentDirectory.Handle;2774 return dirOpenFileWindowsInner(t, null, prefixed_path_w.span(), flags);
27042775 },
2705 return dirOpenFileWindowsInner(t, .{ .handle = cwd_handle }, image_path_name, flags);2776 else => @panic("TODO implement openSelfExe"),
2706 }2777 }
2707 @panic("TODO implement openSelfExe");
2708}2778}
27092779
2710fn fileWritePositional(2780fn fileWritePositional(
...@@ -2823,7 +2893,8 @@ fn sleepWindows(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {...@@ -2823,7 +2893,8 @@ fn sleepWindows(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
2823 break :ms std.math.maxInt(windows.DWORD);2893 break :ms std.math.maxInt(windows.DWORD);
2824 break :ms std.math.lossyCast(windows.DWORD, d.raw.toMilliseconds());2894 break :ms std.math.lossyCast(windows.DWORD, d.raw.toMilliseconds());
2825 };2895 };
2826 windows.kernel32.Sleep(ms);2896 // TODO: alertable true with checkCancel in a loop plus deadline
2897 _ = windows.kernel32.SleepEx(ms, windows.FALSE);
2827}2898}
28282899
2829fn sleepWasi(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {2900fn sleepWasi(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
lib/std/os/windows.zig+1-1
...@@ -148,7 +148,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN...@@ -148,7 +148,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
148 // call has failed. There is not really a sane way to handle148 // call has failed. There is not really a sane way to handle
149 // this other than retrying the creation after the OS finishes149 // this other than retrying the creation after the OS finishes
150 // the deletion.150 // the deletion.
151 kernel32.Sleep(1);151 _ = kernel32.SleepEx(1, TRUE);
152 continue;152 continue;
153 },153 },
154 .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference,154 .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference,
lib/std/os/windows/kernel32.zig+4-3
...@@ -326,10 +326,11 @@ pub extern "kernel32" fn ExitProcess(...@@ -326,10 +326,11 @@ pub extern "kernel32" fn ExitProcess(
326 exit_code: UINT,326 exit_code: UINT,
327) callconv(.winapi) noreturn;327) callconv(.winapi) noreturn;
328328
329// TODO: SleepEx with bAlertable=false.329// TODO: implement via ntdll instead
330pub extern "kernel32" fn Sleep(330pub extern "kernel32" fn SleepEx(
331 dwMilliseconds: DWORD,331 dwMilliseconds: DWORD,
332) callconv(.winapi) void;332 bAlertable: BOOL,
333) callconv(.winapi) DWORD;
333334
334// TODO: Wrapper around NtQueryInformationProcess with `PROCESS_BASIC_INFORMATION`.335// TODO: Wrapper around NtQueryInformationProcess with `PROCESS_BASIC_INFORMATION`.
335pub extern "kernel32" fn GetExitCodeProcess(336pub extern "kernel32" fn GetExitCodeProcess(