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(
20442044 const t: *Threaded = @ptrCast(@alignCast(userdata));
20452045 const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
20462046 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);
20482049}
20492050
20502051fn dirOpenFileWindowsInner(
20512052 t: *Threaded,
2052 dir: Io.Dir,
2053 dir_handle: ?windows.HANDLE,
20532054 sub_path_w: [:0]const u16,
20542055 flags: Io.File.OpenFlags,
20552056) 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
20572061 const w = windows;
2058 const handle = try w.OpenFile(sub_path_w, .{
2059 .dir = dir.handle,
2060 .access_mask = w.SYNCHRONIZE |
2061 (if (flags.isRead()) @as(u32, w.GENERIC_READ) else 0) |
2062 (if (flags.isWrite()) @as(u32, w.GENERIC_WRITE) else 0),
2063 .creation = w.FILE_OPEN,
2064 });
2065 errdefer w.CloseHandle(handle);
2062
2063 var nt_name: w.UNICODE_STRING = .{
2064 .Length = path_len_bytes,
2065 .MaximumLength = path_len_bytes,
2066 .Buffer = @constCast(sub_path_w.ptr),
2067 };
2068 var attr: w.OBJECT_ATTRIBUTES = .{
2069 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
2070 .RootDirectory = dir_handle,
2071 .Attributes = 0,
2072 .ObjectName = &nt_name,
2073 .SecurityDescriptor = null,
2074 .SecurityQualityOfService = null,
2075 };
20662076 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
20672138 const range_off: w.LARGE_INTEGER = 0;
20682139 const range_len: w.LARGE_INTEGER = 1;
20692140 const exclusive = switch (flags.lock) {
......@@ -2691,20 +2762,19 @@ fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekErr
26912762
26922763fn openSelfExe(userdata: ?*anyopaque, flags: Io.File.OpenFlags) Io.File.OpenSelfExeError!Io.File {
26932764 const t: *Threaded = @ptrCast(@alignCast(userdata));
2694 if (native_os == .linux or native_os == .serenity) {
2695 return dirOpenFilePosix(t, .{ .handle = posix.AT.FDCWD }, "/proc/self/exe", flags);
2696 }
2697 if (is_windows) {
2698 // If ImagePathName is a symlink, then it will contain the path of the symlink,
2699 // not the path that the symlink points to. However, because we are opening
2700 // the file, we can let the openFileW call follow the symlink for us.
2701 const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
2702 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
2703 const cwd_handle = std.os.windows.peb().ProcessParameters.CurrentDirectory.Handle;
2704
2705 return dirOpenFileWindowsInner(t, .{ .handle = cwd_handle }, image_path_name, flags);
2765 switch (native_os) {
2766 .linux, .serenity => return dirOpenFilePosix(t, .{ .handle = posix.AT.FDCWD }, "/proc/self/exe", flags),
2767 .windows => {
2768 // 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
2770 // the file, we can let the openFileW call follow the symlink for us.
2771 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];
2773 const prefixed_path_w = try windows.wToPrefixedFileW(null, image_path_name);
2774 return dirOpenFileWindowsInner(t, null, prefixed_path_w.span(), flags);
2775 },
2776 else => @panic("TODO implement openSelfExe"),
27062777 }
2707 @panic("TODO implement openSelfExe");
27082778}
27092779
27102780fn fileWritePositional(
......@@ -2823,7 +2893,8 @@ fn sleepWindows(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
28232893 break :ms std.math.maxInt(windows.DWORD);
28242894 break :ms std.math.lossyCast(windows.DWORD, d.raw.toMilliseconds());
28252895 };
2826 windows.kernel32.Sleep(ms);
2896 // TODO: alertable true with checkCancel in a loop plus deadline
2897 _ = windows.kernel32.SleepEx(ms, windows.FALSE);
28272898}
28282899
28292900fn 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
148148 // call has failed. There is not really a sane way to handle
149149 // this other than retrying the creation after the OS finishes
150150 // the deletion.
151 kernel32.Sleep(1);
151 _ = kernel32.SleepEx(1, TRUE);
152152 continue;
153153 },
154154 .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference,
lib/std/os/windows/kernel32.zig+4-3
......@@ -326,10 +326,11 @@ pub extern "kernel32" fn ExitProcess(
326326 exit_code: UINT,
327327) callconv(.winapi) noreturn;
328328
329// TODO: SleepEx with bAlertable=false.
330pub extern "kernel32" fn Sleep(
329// TODO: implement via ntdll instead
330pub extern "kernel32" fn SleepEx(
331331 dwMilliseconds: DWORD,
332) callconv(.winapi) void;
332 bAlertable: BOOL,
333) callconv(.winapi) DWORD;
333334
334335// TODO: Wrapper around NtQueryInformationProcess with `PROCESS_BASIC_INFORMATION`.
335336pub extern "kernel32" fn GetExitCodeProcess(