authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-19 22:17:45-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:50-07:00
log6d1b2c7f64fd1a1c671f357cff96b3dd39612857
tree996e692c2b132f48827de477da5a8777bd8ee1fb
parent1c67607397be4efa962d64a1d80b7fd91fe161eb

std.Io: introduce openSelfExe


6 files changed, 61 insertions(+), 29 deletions(-)

lib/std/Io.zig+1
...@@ -678,6 +678,7 @@ pub const VTable = struct {...@@ -678,6 +678,7 @@ pub const VTable = struct {
678 fileReadPositional: *const fn (?*anyopaque, File, data: [][]u8, offset: u64) File.ReadPositionalError!usize,678 fileReadPositional: *const fn (?*anyopaque, File, data: [][]u8, offset: u64) File.ReadPositionalError!usize,
679 fileSeekBy: *const fn (?*anyopaque, File, relative_offset: i64) File.SeekError!void,679 fileSeekBy: *const fn (?*anyopaque, File, relative_offset: i64) File.SeekError!void,
680 fileSeekTo: *const fn (?*anyopaque, File, absolute_offset: u64) File.SeekError!void,680 fileSeekTo: *const fn (?*anyopaque, File, absolute_offset: u64) File.SeekError!void,
681 openSelfExe: *const fn (?*anyopaque, File.OpenFlags) File.OpenSelfExeError!File,
681682
682 now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp,683 now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp,
683 sleep: *const fn (?*anyopaque, Timeout) SleepError!void,684 sleep: *const fn (?*anyopaque, Timeout) SleepError!void,
lib/std/Io/Dir.zig+16-1
...@@ -1,5 +1,8 @@...@@ -1,5 +1,8 @@
1const Dir = @This();1const Dir = @This();
22
3const builtin = @import("builtin");
4const native_os = builtin.os.tag;
5
3const std = @import("../std.zig");6const std = @import("../std.zig");
4const Io = std.Io;7const Io = std.Io;
5const File = Io.File;8const File = Io.File;
...@@ -9,8 +12,20 @@ handle: Handle,...@@ -9,8 +12,20 @@ handle: Handle,
9pub const Mode = Io.File.Mode;12pub const Mode = Io.File.Mode;
10pub const default_mode: Mode = 0o755;13pub const default_mode: Mode = 0o755;
1114
15/// Returns a handle to the current working directory.
16///
17/// It is not opened with iteration capability. Iterating over the result is
18/// illegal behavior.
19///
20/// Closing the returned `Dir` is checked illegal behavior.
21///
22/// On POSIX targets, this function is comptime-callable.
12pub fn cwd() Dir {23pub fn cwd() Dir {
13 return .{ .handle = std.fs.cwd().fd };24 return switch (native_os) {
25 .windows => .{ .handle = std.os.windows.peb().ProcessParameters.CurrentDirectory.Handle },
26 .wasi => .{ .handle = std.options.wasiCwd() },
27 else => .{ .handle = std.posix.AT.FDCWD },
28 };
14}29}
1530
16pub const Handle = std.posix.fd_t;31pub const Handle = std.posix.fd_t;
lib/std/Io/File.zig+6
...@@ -207,6 +207,12 @@ pub fn close(file: File, io: Io) void {...@@ -207,6 +207,12 @@ pub fn close(file: File, io: Io) void {
207 return io.vtable.fileClose(io.userdata, file);207 return io.vtable.fileClose(io.userdata, file);
208}208}
209209
210pub const OpenSelfExeError = OpenError || std.fs.SelfExePathError || std.posix.FlockError;
211
212pub fn openSelfExe(io: Io, flags: OpenFlags) OpenSelfExeError!File {
213 return io.vtable.openSelfExe(io.userdata, flags);
214}
215
210pub const ReadStreamingError = error{216pub const ReadStreamingError = error{
211 InputOutput,217 InputOutput,
212 SystemResources,218 SystemResources,
lib/std/Io/Threaded.zig+21
...@@ -209,6 +209,7 @@ pub fn io(t: *Threaded) Io {...@@ -209,6 +209,7 @@ pub fn io(t: *Threaded) Io {
209 .fileReadPositional = fileReadPositional,209 .fileReadPositional = fileReadPositional,
210 .fileSeekBy = fileSeekBy,210 .fileSeekBy = fileSeekBy,
211 .fileSeekTo = fileSeekTo,211 .fileSeekTo = fileSeekTo,
212 .openSelfExe = openSelfExe,
212213
213 .now = switch (builtin.os.tag) {214 .now = switch (builtin.os.tag) {
214 .windows => nowWindows,215 .windows => nowWindows,
...@@ -2241,6 +2242,26 @@ fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekErr...@@ -2241,6 +2242,26 @@ fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekErr
2241 }2242 }
2242}2243}
22432244
2245fn openSelfExe(userdata: ?*anyopaque, flags: Io.File.OpenFlags) Io.File.OpenSelfExeError!Io.File {
2246 const t: *Threaded = @ptrCast(@alignCast(userdata));
2247 if (native_os == .linux or native_os == .serenity) {
2248 return dirOpenFilePosix(t, .{ .handle = posix.AT.FDCWD }, "/proc/self/exe", flags);
2249 }
2250 if (is_windows) {
2251 // If ImagePathName is a symlink, then it will contain the path of the symlink,
2252 // not the path that the symlink points to. However, because we are opening
2253 // the file, we can let the openFileW call follow the symlink for us.
2254 const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
2255 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
2256 const prefixed_path_w_array = try windows.wToPrefixedFileW(null, image_path_name);
2257 const prefixed_path_w = prefixed_path_w_array.span();
2258 const cwd_handle = std.os.windows.peb().ProcessParameters.CurrentDirectory.Handle;
2259
2260 return dirOpenFileWindows(t, .{ .handle = cwd_handle }, prefixed_path_w, flags);
2261 }
2262 @panic("TODO");
2263}
2264
2244fn fileWritePositional(2265fn fileWritePositional(
2245 userdata: ?*anyopaque,2266 userdata: ?*anyopaque,
2246 file: Io.File,2267 file: Io.File,
lib/std/debug/SelfInfo/Windows.zig+10-13
...@@ -297,17 +297,7 @@ const Module = struct {...@@ -297,17 +297,7 @@ const Module = struct {
297 // a binary is produced with -gdwarf, since the section names are longer than 8 bytes.297 // a binary is produced with -gdwarf, since the section names are longer than 8 bytes.
298 const mapped_file: ?DebugInfo.MappedFile = mapped: {298 const mapped_file: ?DebugInfo.MappedFile = mapped: {
299 if (!coff_obj.strtabRequired()) break :mapped null;299 if (!coff_obj.strtabRequired()) break :mapped null;
300 var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined;300 const coff_file = Io.File.openSelfExe(io, .{}) catch |err| switch (err) {
301 name_buffer[0..4].* = .{ '\\', '?', '?', '\\' }; // openFileAbsoluteW requires the prefix to be present
302 const process_handle = windows.GetCurrentProcess();
303 const len = windows.kernel32.GetModuleFileNameExW(
304 process_handle,
305 module.handle,
306 name_buffer[4..],
307 windows.PATH_MAX_WIDE,
308 );
309 if (len == 0) return error.MissingDebugInfo;
310 const coff_file = fs.openFileAbsoluteW(name_buffer[0 .. len + 4 :0], .{}) catch |err| switch (err) {
311 error.Canceled => |e| return e,301 error.Canceled => |e| return e,
312 error.Unexpected => |e| return e,302 error.Unexpected => |e| return e,
313 error.FileNotFound => return error.MissingDebugInfo,303 error.FileNotFound => return error.MissingDebugInfo,
...@@ -337,9 +327,15 @@ const Module = struct {...@@ -337,9 +327,15 @@ const Module = struct {
337 error.SystemFdQuotaExceeded,327 error.SystemFdQuotaExceeded,
338 error.FileLocksNotSupported,328 error.FileLocksNotSupported,
339 error.FileBusy,329 error.FileBusy,
330 error.InputOutput,
331 error.NotSupported,
332 error.FileSystem,
333 error.NotLink,
334 error.UnrecognizedVolume,
335 error.UnknownName,
340 => return error.ReadFailed,336 => return error.ReadFailed,
341 };337 };
342 errdefer coff_file.close();338 errdefer coff_file.close(io);
343 var section_handle: windows.HANDLE = undefined;339 var section_handle: windows.HANDLE = undefined;
344 const create_section_rc = windows.ntdll.NtCreateSection(340 const create_section_rc = windows.ntdll.NtCreateSection(
345 &section_handle,341 &section_handle,
...@@ -356,6 +352,7 @@ const Module = struct {...@@ -356,6 +352,7 @@ const Module = struct {
356 errdefer windows.CloseHandle(section_handle);352 errdefer windows.CloseHandle(section_handle);
357 var coff_len: usize = 0;353 var coff_len: usize = 0;
358 var section_view_ptr: ?[*]const u8 = null;354 var section_view_ptr: ?[*]const u8 = null;
355 const process_handle = windows.GetCurrentProcess();
359 const map_section_rc = windows.ntdll.NtMapViewOfSection(356 const map_section_rc = windows.ntdll.NtMapViewOfSection(
360 section_handle,357 section_handle,
361 process_handle,358 process_handle,
...@@ -373,7 +370,7 @@ const Module = struct {...@@ -373,7 +370,7 @@ const Module = struct {
373 const section_view = section_view_ptr.?[0..coff_len];370 const section_view = section_view_ptr.?[0..coff_len];
374 coff_obj = coff.Coff.init(section_view, false) catch return error.InvalidDebugInfo;371 coff_obj = coff.Coff.init(section_view, false) catch return error.InvalidDebugInfo;
375 break :mapped .{372 break :mapped .{
376 .file = coff_file,373 .file = .adaptFromNewApi(coff_file),
377 .section_handle = section_handle,374 .section_handle = section_handle,
378 .section_view = section_view,375 .section_view = section_view,
379 };376 };
lib/std/fs.zig+7-15
...@@ -180,9 +180,7 @@ pub fn renameZ(old_dir: Dir, old_sub_path_z: [*:0]const u8, new_dir: Dir, new_su...@@ -180,9 +180,7 @@ pub fn renameZ(old_dir: Dir, old_sub_path_z: [*:0]const u8, new_dir: Dir, new_su
180 return posix.renameatZ(old_dir.fd, old_sub_path_z, new_dir.fd, new_sub_path_z);180 return posix.renameatZ(old_dir.fd, old_sub_path_z, new_dir.fd, new_sub_path_z);
181}181}
182182
183/// Returns a handle to the current working directory. It is not opened with iteration capability.183/// Deprecated in favor of `Io.Dir.cwd`.
184/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.
185/// On POSIX targets, this function is comptime-callable.
186pub fn cwd() Dir {184pub fn cwd() Dir {
187 if (native_os == .windows) {185 if (native_os == .windows) {
188 return .{ .fd = windows.peb().ProcessParameters.CurrentDirectory.Handle };186 return .{ .fd = windows.peb().ProcessParameters.CurrentDirectory.Handle };
...@@ -336,20 +334,14 @@ pub fn symLinkAbsoluteW(...@@ -336,20 +334,14 @@ pub fn symLinkAbsoluteW(
336 return windows.CreateSymbolicLink(null, mem.span(sym_link_path_w), mem.span(target_path_w), flags.is_directory);334 return windows.CreateSymbolicLink(null, mem.span(sym_link_path_w), mem.span(target_path_w), flags.is_directory);
337}335}
338336
339pub const OpenSelfExeError = posix.OpenError || SelfExePathError || posix.FlockError;337pub const OpenSelfExeError = Io.File.OpenSelfExeError;
340338
339/// Deprecated in favor of `Io.File.openSelfExe`.
341pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {340pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {
342 if (native_os == .linux or native_os == .serenity) {341 if (native_os == .linux or native_os == .serenity or native_os == .windows) {
343 return openFileAbsolute("/proc/self/exe", flags);342 var threaded: Io.Threaded = .init_single_threaded;
344 }343 const io = threaded.io();
345 if (native_os == .windows) {344 return .adaptFromNewApi(try Io.File.openSelfExe(io, flags));
346 // If ImagePathName is a symlink, then it will contain the path of the symlink,
347 // not the path that the symlink points to. However, because we are opening
348 // the file, we can let the openFileW call follow the symlink for us.
349 const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
350 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
351 const prefixed_path_w = try windows.wToPrefixedFileW(null, image_path_name);
352 return cwd().openFileW(prefixed_path_w.span(), flags);
353 }345 }
354 // Use of max_path_bytes here is valid as the resulting path is immediately346 // Use of max_path_bytes here is valid as the resulting path is immediately
355 // opened with no modification.347 // opened with no modification.