From 6d1b2c7f64fd1a1c671f357cff96b3dd39612857 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 19 Oct 2025 22:17:45 -0700 Subject: [PATCH] std.Io: introduce openSelfExe --- lib/std/Io.zig | 1 + lib/std/Io/Dir.zig | 17 ++++++++++++++++- lib/std/Io/File.zig | 6 ++++++ lib/std/Io/Threaded.zig | 21 +++++++++++++++++++++ lib/std/debug/SelfInfo/Windows.zig | 23 ++++++++++------------- lib/std/fs.zig | 22 +++++++--------------- 6 files changed, 61 insertions(+), 29 deletions(-) diff --git a/lib/std/Io.zig b/lib/std/Io.zig index a6507df618..b16661be97 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -678,6 +678,7 @@ pub const VTable = struct { fileReadPositional: *const fn (?*anyopaque, File, data: [][]u8, offset: u64) File.ReadPositionalError!usize, fileSeekBy: *const fn (?*anyopaque, File, relative_offset: i64) File.SeekError!void, fileSeekTo: *const fn (?*anyopaque, File, absolute_offset: u64) File.SeekError!void, + openSelfExe: *const fn (?*anyopaque, File.OpenFlags) File.OpenSelfExeError!File, now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, sleep: *const fn (?*anyopaque, Timeout) SleepError!void, diff --git a/lib/std/Io/Dir.zig b/lib/std/Io/Dir.zig index ab8f75e541..7336ab24af 100644 --- a/lib/std/Io/Dir.zig +++ b/lib/std/Io/Dir.zig @@ -1,5 +1,8 @@ const Dir = @This(); +const builtin = @import("builtin"); +const native_os = builtin.os.tag; + const std = @import("../std.zig"); const Io = std.Io; const File = Io.File; @@ -9,8 +12,20 @@ handle: Handle, pub const Mode = Io.File.Mode; pub const default_mode: Mode = 0o755; +/// Returns a handle to the current working directory. +/// +/// It is not opened with iteration capability. Iterating over the result is +/// illegal behavior. +/// +/// Closing the returned `Dir` is checked illegal behavior. +/// +/// On POSIX targets, this function is comptime-callable. pub fn cwd() Dir { - return .{ .handle = std.fs.cwd().fd }; + return switch (native_os) { + .windows => .{ .handle = std.os.windows.peb().ProcessParameters.CurrentDirectory.Handle }, + .wasi => .{ .handle = std.options.wasiCwd() }, + else => .{ .handle = std.posix.AT.FDCWD }, + }; } pub const Handle = std.posix.fd_t; diff --git a/lib/std/Io/File.zig b/lib/std/Io/File.zig index 51f3a08df7..29500ffb1d 100644 --- a/lib/std/Io/File.zig +++ b/lib/std/Io/File.zig @@ -207,6 +207,12 @@ pub fn close(file: File, io: Io) void { return io.vtable.fileClose(io.userdata, file); } +pub const OpenSelfExeError = OpenError || std.fs.SelfExePathError || std.posix.FlockError; + +pub fn openSelfExe(io: Io, flags: OpenFlags) OpenSelfExeError!File { + return io.vtable.openSelfExe(io.userdata, flags); +} + pub const ReadStreamingError = error{ InputOutput, SystemResources, diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 7f78c617a5..174d4b76f3 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -209,6 +209,7 @@ pub fn io(t: *Threaded) Io { .fileReadPositional = fileReadPositional, .fileSeekBy = fileSeekBy, .fileSeekTo = fileSeekTo, + .openSelfExe = openSelfExe, .now = switch (builtin.os.tag) { .windows => nowWindows, @@ -2241,6 +2242,26 @@ fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekErr } } +fn openSelfExe(userdata: ?*anyopaque, flags: Io.File.OpenFlags) Io.File.OpenSelfExeError!Io.File { + const t: *Threaded = @ptrCast(@alignCast(userdata)); + if (native_os == .linux or native_os == .serenity) { + return dirOpenFilePosix(t, .{ .handle = posix.AT.FDCWD }, "/proc/self/exe", flags); + } + if (is_windows) { + // If ImagePathName is a symlink, then it will contain the path of the symlink, + // not the path that the symlink points to. However, because we are opening + // the file, we can let the openFileW call follow the symlink for us. + const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName; + const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0]; + const prefixed_path_w_array = try windows.wToPrefixedFileW(null, image_path_name); + const prefixed_path_w = prefixed_path_w_array.span(); + const cwd_handle = std.os.windows.peb().ProcessParameters.CurrentDirectory.Handle; + + return dirOpenFileWindows(t, .{ .handle = cwd_handle }, prefixed_path_w, flags); + } + @panic("TODO"); +} + fn fileWritePositional( userdata: ?*anyopaque, file: Io.File, diff --git a/lib/std/debug/SelfInfo/Windows.zig b/lib/std/debug/SelfInfo/Windows.zig index a0b26f8ec5..51c41030dc 100644 --- a/lib/std/debug/SelfInfo/Windows.zig +++ b/lib/std/debug/SelfInfo/Windows.zig @@ -297,17 +297,7 @@ const Module = struct { // a binary is produced with -gdwarf, since the section names are longer than 8 bytes. const mapped_file: ?DebugInfo.MappedFile = mapped: { if (!coff_obj.strtabRequired()) break :mapped null; - var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined; - name_buffer[0..4].* = .{ '\\', '?', '?', '\\' }; // openFileAbsoluteW requires the prefix to be present - const process_handle = windows.GetCurrentProcess(); - const len = windows.kernel32.GetModuleFileNameExW( - process_handle, - module.handle, - name_buffer[4..], - windows.PATH_MAX_WIDE, - ); - if (len == 0) return error.MissingDebugInfo; - const coff_file = fs.openFileAbsoluteW(name_buffer[0 .. len + 4 :0], .{}) catch |err| switch (err) { + const coff_file = Io.File.openSelfExe(io, .{}) catch |err| switch (err) { error.Canceled => |e| return e, error.Unexpected => |e| return e, error.FileNotFound => return error.MissingDebugInfo, @@ -337,9 +327,15 @@ const Module = struct { error.SystemFdQuotaExceeded, error.FileLocksNotSupported, error.FileBusy, + error.InputOutput, + error.NotSupported, + error.FileSystem, + error.NotLink, + error.UnrecognizedVolume, + error.UnknownName, => return error.ReadFailed, }; - errdefer coff_file.close(); + errdefer coff_file.close(io); var section_handle: windows.HANDLE = undefined; const create_section_rc = windows.ntdll.NtCreateSection( §ion_handle, @@ -356,6 +352,7 @@ const Module = struct { errdefer windows.CloseHandle(section_handle); var coff_len: usize = 0; var section_view_ptr: ?[*]const u8 = null; + const process_handle = windows.GetCurrentProcess(); const map_section_rc = windows.ntdll.NtMapViewOfSection( section_handle, process_handle, @@ -373,7 +370,7 @@ const Module = struct { const section_view = section_view_ptr.?[0..coff_len]; coff_obj = coff.Coff.init(section_view, false) catch return error.InvalidDebugInfo; break :mapped .{ - .file = coff_file, + .file = .adaptFromNewApi(coff_file), .section_handle = section_handle, .section_view = section_view, }; diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 25018ddaa8..ea39ce8e33 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -180,9 +180,7 @@ pub fn renameZ(old_dir: Dir, old_sub_path_z: [*:0]const u8, new_dir: Dir, new_su return posix.renameatZ(old_dir.fd, old_sub_path_z, new_dir.fd, new_sub_path_z); } -/// Returns a handle to the current working directory. It is not opened with iteration capability. -/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior. -/// On POSIX targets, this function is comptime-callable. +/// Deprecated in favor of `Io.Dir.cwd`. pub fn cwd() Dir { if (native_os == .windows) { return .{ .fd = windows.peb().ProcessParameters.CurrentDirectory.Handle }; @@ -336,20 +334,14 @@ pub fn symLinkAbsoluteW( return windows.CreateSymbolicLink(null, mem.span(sym_link_path_w), mem.span(target_path_w), flags.is_directory); } -pub const OpenSelfExeError = posix.OpenError || SelfExePathError || posix.FlockError; +pub const OpenSelfExeError = Io.File.OpenSelfExeError; +/// Deprecated in favor of `Io.File.openSelfExe`. pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File { - if (native_os == .linux or native_os == .serenity) { - return openFileAbsolute("/proc/self/exe", flags); - } - if (native_os == .windows) { - // If ImagePathName is a symlink, then it will contain the path of the symlink, - // not the path that the symlink points to. However, because we are opening - // the file, we can let the openFileW call follow the symlink for us. - const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName; - const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0]; - const prefixed_path_w = try windows.wToPrefixedFileW(null, image_path_name); - return cwd().openFileW(prefixed_path_w.span(), flags); + if (native_os == .linux or native_os == .serenity or native_os == .windows) { + var threaded: Io.Threaded = .init_single_threaded; + const io = threaded.io(); + return .adaptFromNewApi(try Io.File.openSelfExe(io, flags)); } // Use of max_path_bytes here is valid as the resulting path is immediately // opened with no modification.