authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-06 20:43:57-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-23 22:15:08-08:00
log877032ec6a0007316f42658d12042f1473de4856
treed6541c2050b24f97ef80895061e881bc31fc5449
parent8328de24f13e21e325207b19288a143854df50df

std: reorganize realpath and process executable APIs


12 files changed, 333 insertions(+), 315 deletions(-)

lib/std/Io.zig+3-1
...@@ -697,7 +697,6 @@ pub const VTable = struct {...@@ -697,7 +697,6 @@ pub const VTable = struct {
697 fileReadPositional: *const fn (?*anyopaque, File, data: [][]u8, offset: u64) File.ReadPositionalError!usize,697 fileReadPositional: *const fn (?*anyopaque, File, data: [][]u8, offset: u64) File.ReadPositionalError!usize,
698 fileSeekBy: *const fn (?*anyopaque, File, relative_offset: i64) File.SeekError!void,698 fileSeekBy: *const fn (?*anyopaque, File, relative_offset: i64) File.SeekError!void,
699 fileSeekTo: *const fn (?*anyopaque, File, absolute_offset: u64) File.SeekError!void,699 fileSeekTo: *const fn (?*anyopaque, File, absolute_offset: u64) File.SeekError!void,
700 openSelfExe: *const fn (?*anyopaque, File.OpenFlags) File.OpenSelfExeError!File,
701 fileSync: *const fn (?*anyopaque, File) File.SyncError!void,700 fileSync: *const fn (?*anyopaque, File) File.SyncError!void,
702 fileIsTty: *const fn (?*anyopaque, File) Cancelable!bool,701 fileIsTty: *const fn (?*anyopaque, File) Cancelable!bool,
703 fileEnableAnsiEscapeCodes: *const fn (?*anyopaque, File) File.EnableAnsiEscapeCodesError!void,702 fileEnableAnsiEscapeCodes: *const fn (?*anyopaque, File) File.EnableAnsiEscapeCodesError!void,
...@@ -712,6 +711,9 @@ pub const VTable = struct {...@@ -712,6 +711,9 @@ pub const VTable = struct {
712 fileUnlock: *const fn (?*anyopaque, File) void,711 fileUnlock: *const fn (?*anyopaque, File) void,
713 fileDowngradeLock: *const fn (?*anyopaque, File) File.DowngradeLockError!void,712 fileDowngradeLock: *const fn (?*anyopaque, File) File.DowngradeLockError!void,
714713
714 processExecutableOpen: *const fn (?*anyopaque, File.OpenFlags) std.process.OpenExecutableError!File,
715 processExecutablePath: *const fn (?*anyopaque, buffer: []u8) std.process.ExecutablePathError!usize,
716
715 now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp,717 now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp,
716 sleep: *const fn (?*anyopaque, Timeout) SleepError!void,718 sleep: *const fn (?*anyopaque, Timeout) SleepError!void,
717719
lib/std/Io/Dir.zig+27-12
...@@ -694,24 +694,29 @@ pub const RealPathError = error{...@@ -694,24 +694,29 @@ pub const RealPathError = error{
694/// supported hosts are: Linux, macOS, and Windows.694/// supported hosts are: Linux, macOS, and Windows.
695///695///
696/// See also:696/// See also:
697/// * `realpathAlloc`.697/// * `realPathAlloc`.
698pub fn realPath(dir: Dir, io: Io, sub_path: []const u8, out_buffer: []u8) RealPathError!usize {698pub fn realPath(dir: Dir, io: Io, sub_path: []const u8, out_buffer: []u8) RealPathError!usize {
699 return io.vtable.dirRealPath(io.userdata, dir, sub_path, out_buffer);699 return io.vtable.dirRealPath(io.userdata, dir, sub_path, out_buffer);
700}700}
701701
702pub const RealPathAllocError = RealPathError || Allocator.Error;702pub const RealPathAllocError = RealPathError || Allocator.Error;
703703
704/// Same as `Dir.realpath` except caller must free the returned memory.704/// Same as `realPath` except allocates result.
705/// See also `Dir.realpath`.705pub fn realPathAlloc(dir: Dir, io: Io, sub_path: []const u8, allocator: Allocator) RealPathAllocError![:0]u8 {
706pub fn realpathAlloc(self: Dir, allocator: Allocator, pathname: []const u8) RealPathAllocError![]u8 {706 var buffer: [std.fs.max_path_bytes]u8 = undefined;
707 // Use of max_path_bytes here is valid as the realpath function does not707 const n = try realPath(dir, io, sub_path, &buffer);
708 // have a variant that takes an arbitrary-size buffer.708 return allocator.dupeZ(u8, buffer[0..n]);
709 // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008709}
710 // NULL out parameter (GNU's canonicalize_file_name) to handle overelong710
711 // paths. musl supports passing NULL but restricts the output to PATH_MAX711pub fn realPathAbsolute(io: Io, path: []const u8, out_buffer: []u8) RealPathError!usize {
712 // anyway.712 return io.vtable.dirRealPath(io.userdata, .cwd(), path, out_buffer);
713 var buf: [std.fs.max_path_bytes]u8 = undefined;713}
714 return allocator.dupe(u8, try self.realpath(pathname, &buf));714
715/// Same as `realPathAbsolute` except allocates result.
716pub fn realPathAbsoluteAlloc(io: Io, path: []const u8, allocator: Allocator) RealPathAllocError![:0]u8 {
717 var buffer: [std.fs.max_path_bytes]u8 = undefined;
718 const n = try realPathAbsolute(io, path, &buffer);
719 return allocator.dupeZ(u8, buffer[0..n]);
715}720}
716721
717pub const DeleteFileError = error{722pub const DeleteFileError = error{
...@@ -975,6 +980,16 @@ pub fn readLink(dir: Dir, io: Io, sub_path: []const u8, buffer: []u8) ReadLinkEr...@@ -975,6 +980,16 @@ pub fn readLink(dir: Dir, io: Io, sub_path: []const u8, buffer: []u8) ReadLinkEr
975 return io.vtable.dirReadLink(io.userdata, dir, sub_path, buffer);980 return io.vtable.dirReadLink(io.userdata, dir, sub_path, buffer);
976}981}
977982
983/// Same as `readLink`, except it asserts the path is absolute.
984///
985/// On Windows, `path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
986/// On WASI, `path` should be encoded as valid UTF-8.
987/// On other platforms, `path` is an opaque sequence of bytes with no particular encoding.
988pub fn readLinkAbsolute(io: Io, path: []const u8, buffer: []u8) ReadLinkError!usize {
989 assert(std.fs.path.isAbsolute(path));
990 return io.vtable.dirReadLink(io.userdata, .cwd(), path, buffer);
991}
992
978pub const ReadFileAllocError = File.OpenError || File.ReadError || Allocator.Error || error{993pub const ReadFileAllocError = File.OpenError || File.ReadError || Allocator.Error || error{
979 /// File size reached or exceeded the provided limit.994 /// File size reached or exceeded the provided limit.
980 StreamTooLong,995 StreamTooLong,
lib/std/Io/File.zig-6
...@@ -464,12 +464,6 @@ pub fn setTimestampsNow(file: File, io: Io) SetTimestampsError!void {...@@ -464,12 +464,6 @@ pub fn setTimestampsNow(file: File, io: Io) SetTimestampsError!void {
464 return io.vtable.fileSetTimestampsNow(io.userdata, file);464 return io.vtable.fileSetTimestampsNow(io.userdata, file);
465}465}
466466
467pub const OpenSelfExeError = OpenError || std.fs.SelfExePathError || LockError;
468
469pub fn openSelfExe(io: Io, flags: OpenFlags) OpenSelfExeError!File {
470 return io.vtable.openSelfExe(io.userdata, flags);
471}
472
473pub const ReadPositionalError = Reader.Error || error{Unseekable};467pub const ReadPositionalError = Reader.Error || error{Unseekable};
474468
475pub fn readPositional(file: File, io: Io, buffer: [][]u8, offset: u64) ReadPositionalError!usize {469pub fn readPositional(file: File, io: Io, buffer: [][]u8, offset: u64) ReadPositionalError!usize {
lib/std/Io/Kqueue.zig+2-2
...@@ -888,7 +888,7 @@ pub fn io(k: *Kqueue) Io {...@@ -888,7 +888,7 @@ pub fn io(k: *Kqueue) Io {
888 .fileReadPositional = fileReadPositional,888 .fileReadPositional = fileReadPositional,
889 .fileSeekBy = fileSeekBy,889 .fileSeekBy = fileSeekBy,
890 .fileSeekTo = fileSeekTo,890 .fileSeekTo = fileSeekTo,
891 .openSelfExe = openSelfExe,891 .openExecutable = openExecutable,
892892
893 .now = now,893 .now = now,
894 .sleep = sleep,894 .sleep = sleep,
...@@ -1246,7 +1246,7 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, absolute_offset: u64) File.Seek...@@ -1246,7 +1246,7 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, absolute_offset: u64) File.Seek
1246 _ = absolute_offset;1246 _ = absolute_offset;
1247 @panic("TODO");1247 @panic("TODO");
1248}1248}
1249fn openSelfExe(userdata: ?*anyopaque, file: File.OpenFlags) File.OpenSelfExeError!File {1249fn openExecutable(userdata: ?*anyopaque, file: File.OpenFlags) File.OpenExecutableError!File {
1250 const k: *Kqueue = @ptrCast(@alignCast(userdata));1250 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1251 _ = k;1251 _ = k;
1252 _ = file;1252 _ = file;
lib/std/Io/Threaded.zig+148-4
...@@ -717,7 +717,6 @@ pub fn io(t: *Threaded) Io {...@@ -717,7 +717,6 @@ pub fn io(t: *Threaded) Io {
717 .fileReadPositional = fileReadPositional,717 .fileReadPositional = fileReadPositional,
718 .fileSeekBy = fileSeekBy,718 .fileSeekBy = fileSeekBy,
719 .fileSeekTo = fileSeekTo,719 .fileSeekTo = fileSeekTo,
720 .openSelfExe = openSelfExe,
721 .fileSync = fileSync,720 .fileSync = fileSync,
722 .fileIsTty = fileIsTty,721 .fileIsTty = fileIsTty,
723 .fileEnableAnsiEscapeCodes = fileEnableAnsiEscapeCodes,722 .fileEnableAnsiEscapeCodes = fileEnableAnsiEscapeCodes,
...@@ -732,6 +731,9 @@ pub fn io(t: *Threaded) Io {...@@ -732,6 +731,9 @@ pub fn io(t: *Threaded) Io {
732 .fileUnlock = fileUnlock,731 .fileUnlock = fileUnlock,
733 .fileDowngradeLock = fileDowngradeLock,732 .fileDowngradeLock = fileDowngradeLock,
734733
734 .processExecutableOpen = processExecutableOpen,
735 .processExecutablePath = processExecutablePath,
736
735 .now = now,737 .now = now,
736 .sleep = sleep,738 .sleep = sleep,
737739
...@@ -839,7 +841,6 @@ pub fn ioBasic(t: *Threaded) Io {...@@ -839,7 +841,6 @@ pub fn ioBasic(t: *Threaded) Io {
839 .fileReadPositional = fileReadPositional,841 .fileReadPositional = fileReadPositional,
840 .fileSeekBy = fileSeekBy,842 .fileSeekBy = fileSeekBy,
841 .fileSeekTo = fileSeekTo,843 .fileSeekTo = fileSeekTo,
842 .openSelfExe = openSelfExe,
843 .fileSync = fileSync,844 .fileSync = fileSync,
844 .fileIsTty = fileIsTty,845 .fileIsTty = fileIsTty,
845 .fileEnableAnsiEscapeCodes = fileEnableAnsiEscapeCodes,846 .fileEnableAnsiEscapeCodes = fileEnableAnsiEscapeCodes,
...@@ -854,6 +855,9 @@ pub fn ioBasic(t: *Threaded) Io {...@@ -854,6 +855,9 @@ pub fn ioBasic(t: *Threaded) Io {
854 .fileUnlock = fileUnlock,855 .fileUnlock = fileUnlock,
855 .fileDowngradeLock = fileDowngradeLock,856 .fileDowngradeLock = fileDowngradeLock,
856857
858 .processExecutableOpen = processExecutableOpen,
859 .processExecutablePath = processExecutablePath,
860
857 .now = now,861 .now = now,
858 .sleep = sleep,862 .sleep = sleep,
859863
...@@ -5932,7 +5936,7 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!voi...@@ -5932,7 +5936,7 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!voi
5932 }5936 }
5933}5937}
59345938
5935fn openSelfExe(userdata: ?*anyopaque, flags: File.OpenFlags) File.OpenSelfExeError!File {5939fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) std.process.OpenExecutableError!File {
5936 const t: *Threaded = @ptrCast(@alignCast(userdata));5940 const t: *Threaded = @ptrCast(@alignCast(userdata));
5937 switch (native_os) {5941 switch (native_os) {
5938 .linux, .serenity => return dirOpenFilePosix(t, .{ .handle = posix.AT.FDCWD }, "/proc/self/exe", flags),5942 .linux, .serenity => return dirOpenFilePosix(t, .{ .handle = posix.AT.FDCWD }, "/proc/self/exe", flags),
...@@ -5945,7 +5949,147 @@ fn openSelfExe(userdata: ?*anyopaque, flags: File.OpenFlags) File.OpenSelfExeErr...@@ -5945,7 +5949,147 @@ fn openSelfExe(userdata: ?*anyopaque, flags: File.OpenFlags) File.OpenSelfExeErr
5945 const prefixed_path_w = try windows.wToPrefixedFileW(null, image_path_name);5949 const prefixed_path_w = try windows.wToPrefixedFileW(null, image_path_name);
5946 return dirOpenFileWtf16(t, null, prefixed_path_w.span(), flags);5950 return dirOpenFileWtf16(t, null, prefixed_path_w.span(), flags);
5947 },5951 },
5948 else => @panic("TODO implement openSelfExe"),5952 else => @panic("TODO implement processExecutableOpen"),
5953 }
5954}
5955
5956fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.ExecutablePathError!usize {
5957 const t: *Threaded = @ptrCast(@alignCast(userdata));
5958 const max_path_bytes = std.fs.max_path_bytes;
5959
5960 switch (native_os) {
5961 .driverkit,
5962 .ios,
5963 .maccatalyst,
5964 .macos,
5965 .tvos,
5966 .visionos,
5967 .watchos,
5968 => {
5969 // Note that _NSGetExecutablePath() will return "a path" to
5970 // the executable not a "real path" to the executable.
5971 var symlink_path_buf: [max_path_bytes:0]u8 = undefined;
5972 var u32_len: u32 = max_path_bytes + 1; // include the sentinel
5973 const rc = std.c._NSGetExecutablePath(&symlink_path_buf, &u32_len);
5974 if (rc != 0) return error.NameTooLong;
5975
5976 var real_path_buf: [max_path_bytes]u8 = undefined;
5977 const real_path = Io.Dir.realPathAbsolute(ioBasic(t), &symlink_path_buf, &real_path_buf) catch |err| switch (err) {
5978 error.NetworkNotFound => unreachable, // Windows-only
5979 else => |e| return e,
5980 };
5981 if (real_path.len > out_buffer.len) return error.NameTooLong;
5982 const result = out_buffer[0..real_path.len];
5983 @memcpy(result, real_path);
5984 return result.len;
5985 },
5986 .linux, .serenity => return Io.Dir.readLinkAbsolute(ioBasic(t), "/proc/self/exe", out_buffer) catch |err| switch (err) {
5987 error.UnsupportedReparsePointType => unreachable, // Windows-only
5988 error.NetworkNotFound => unreachable, // Windows-only
5989 else => |e| return e,
5990 },
5991 .illumos => return Io.Dir.readLinkAbsolute(ioBasic(t), "/proc/self/path/a.out", out_buffer) catch |err| switch (err) {
5992 error.UnsupportedReparsePointType => unreachable, // Windows-only
5993 error.NetworkNotFound => unreachable, // Windows-only
5994 else => |e| return e,
5995 },
5996 .freebsd, .dragonfly => {
5997 const current_thread = Thread.getCurrent(t);
5998 try current_thread.checkCancel();
5999 var mib: [4]c_int = .{ posix.CTL.KERN, posix.KERN.PROC, posix.KERN.PROC_PATHNAME, -1 };
6000 var out_len: usize = out_buffer.len;
6001 try posix.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
6002 return out_len;
6003 },
6004 .netbsd => {
6005 const current_thread = Thread.getCurrent(t);
6006 try current_thread.checkCancel();
6007 var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC_ARGS, -1, posix.KERN.PROC_PATHNAME };
6008 var out_len: usize = out_buffer.len;
6009 try posix.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
6010 return out_len;
6011 },
6012 .openbsd, .haiku => {
6013 // OpenBSD doesn't support getting the path of a running process, so try to guess it
6014 if (std.os.argv.len == 0)
6015 return error.FileNotFound;
6016
6017 const argv0 = std.mem.span(std.os.argv[0]);
6018 if (std.mem.indexOf(u8, argv0, "/") != null) {
6019 // argv[0] is a path (relative or absolute): use realpath(3) directly
6020 var real_path_buf: [max_path_bytes]u8 = undefined;
6021 const real_path = Io.Dir.realPathAbsolute(ioBasic(t), std.os.argv[0], &real_path_buf) catch |err| switch (err) {
6022 error.NetworkNotFound => unreachable, // Windows-only
6023 else => |e| return e,
6024 };
6025 if (real_path.len > out_buffer.len)
6026 return error.NameTooLong;
6027 const result = out_buffer[0..real_path.len];
6028 @memcpy(result, real_path);
6029 return result.len;
6030 } else if (argv0.len != 0) {
6031 // argv[0] is not empty (and not a path): search it inside PATH
6032 const PATH = posix.getenvZ("PATH") orelse return error.FileNotFound;
6033 var path_it = std.mem.tokenizeScalar(u8, PATH, std.fs.path.delimiter);
6034 while (path_it.next()) |a_path| {
6035 var resolved_path_buf: [max_path_bytes - 1:0]u8 = undefined;
6036 const resolved_path = std.fmt.bufPrintSentinel(&resolved_path_buf, "{s}/{s}", .{
6037 a_path, std.os.argv[0],
6038 }, 0) catch continue;
6039
6040 var real_path_buf: [max_path_bytes]u8 = undefined;
6041 if (Io.Dir.realPathAbsolute(ioBasic(t), resolved_path, &real_path_buf)) |real_path| {
6042 // found a file, and hope it is the right file
6043 if (real_path.len > out_buffer.len)
6044 return error.NameTooLong;
6045 const result = out_buffer[0..real_path.len];
6046 @memcpy(result, real_path);
6047 return result.len;
6048 } else |_| continue;
6049 }
6050 }
6051 return error.FileNotFound;
6052 },
6053 .windows => {
6054 const current_thread = Thread.getCurrent(t);
6055 try current_thread.checkCancel();
6056 const w = windows;
6057 const image_path_unicode_string = &w.peb().ProcessParameters.ImagePathName;
6058 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
6059
6060 // If ImagePathName is a symlink, then it will contain the path of the
6061 // symlink, not the path that the symlink points to. We want the path
6062 // that the symlink points to, though, so we need to get the realpath.
6063 var path_name_w = try w.wToPrefixedFileW(null, image_path_name);
6064
6065 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
6066 const share_access = w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE;
6067 const creation = w.FILE_OPEN;
6068 const h_file = blk: {
6069 const res = w.OpenFile(path_name_w.span(), .{
6070 .dir = null,
6071 .access_mask = access_mask,
6072 .share_access = share_access,
6073 .creation = creation,
6074 .filter = .any,
6075 }) catch |err| switch (err) {
6076 error.WouldBlock => unreachable,
6077 else => |e| return e,
6078 };
6079 break :blk res;
6080 };
6081 defer w.CloseHandle(h_file);
6082
6083 const wide_slice = w.GetFinalPathNameByHandle(h_file, .{}, out_buffer);
6084
6085 const len = std.unicode.calcWtf8Len(wide_slice);
6086 if (len > out_buffer.len)
6087 return error.NameTooLong;
6088
6089 const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
6090 return end_index;
6091 },
6092 else => @compileError("unsupported OS"),
5949 }6093 }
5950}6094}
59516095
lib/std/debug/SelfInfo/Elf.zig+1-1
...@@ -329,7 +329,7 @@ const Module = struct {...@@ -329,7 +329,7 @@ const Module = struct {
329 defer file.close(io);329 defer file.close(io);
330 break :res std.debug.ElfFile.load(gpa, file, mod.build_id, &.native(mod.name));330 break :res std.debug.ElfFile.load(gpa, file, mod.build_id, &.native(mod.name));
331 } else res: {331 } else res: {
332 const path = std.fs.selfExePathAlloc(gpa) catch |err| switch (err) {332 const path = std.process.executablePathAlloc(io, gpa) catch |err| switch (err) {
333 error.OutOfMemory => |e| return e,333 error.OutOfMemory => |e| return e,
334 else => return error.ReadFailed,334 else => return error.ReadFailed,
335 };335 };
lib/std/debug/SelfInfo/Windows.zig+1-1
...@@ -434,7 +434,7 @@ const Module = struct {...@@ -434,7 +434,7 @@ const Module = struct {
434 const pdb_file_open_result = if (fs.path.isAbsolute(path)) res: {434 const pdb_file_open_result = if (fs.path.isAbsolute(path)) res: {
435 break :res std.fs.cwd().openFile(io, path, .{});435 break :res std.fs.cwd().openFile(io, path, .{});
436 } else res: {436 } else res: {
437 const self_dir = fs.selfExeDirPathAlloc(gpa) catch |err| switch (err) {437 const self_dir = std.process.executableDirPathAlloc(io, gpa) catch |err| switch (err) {
438 error.OutOfMemory, error.Unexpected => |e| return e,438 error.OutOfMemory, error.Unexpected => |e| return e,
439 else => return error.ReadFailed,439 else => return error.ReadFailed,
440 };440 };
lib/std/fs.zig-232
...@@ -25,13 +25,6 @@ pub const File = std.Io.File;...@@ -25,13 +25,6 @@ pub const File = std.Io.File;
25pub const path = @import("fs/path.zig");25pub const path = @import("fs/path.zig");
26pub const wasi = @import("fs/wasi.zig");26pub const wasi = @import("fs/wasi.zig");
2727
28// TODO audit these APIs with respect to Dir and absolute paths
29
30pub const realpath = posix.realpath;
31pub const realpathZ = posix.realpathZ;
32pub const realpathW = posix.realpathW;
33pub const realpathW2 = posix.realpathW2;
34
35pub const getAppDataDir = @import("fs/get_app_data_dir.zig").getAppDataDir;28pub const getAppDataDir = @import("fs/get_app_data_dir.zig").getAppDataDir;
36pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirError;29pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirError;
3730
...@@ -241,15 +234,6 @@ pub fn deleteTreeAbsolute(io: Io, absolute_path: []const u8) !void {...@@ -241,15 +234,6 @@ pub fn deleteTreeAbsolute(io: Io, absolute_path: []const u8) !void {
241 return dir.deleteTree(path.basename(absolute_path));234 return dir.deleteTree(path.basename(absolute_path));
242}235}
243236
244/// Same as `Dir.readLink`, except it asserts the path is absolute.
245/// On Windows, `pathname` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
246/// On WASI, `pathname` should be encoded as valid UTF-8.
247/// On other platforms, `pathname` is an opaque sequence of bytes with no particular encoding.
248pub fn readLinkAbsolute(pathname: []const u8, buffer: *[max_path_bytes]u8) ![]u8 {
249 assert(path.isAbsolute(pathname));
250 return posix.readlink(pathname, buffer);
251}
252
253/// Creates a symbolic link named `sym_link_path` which contains the string `target_path`.237/// Creates a symbolic link named `sym_link_path` which contains the string `target_path`.
254/// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent238/// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
255/// one; the latter case is known as a dangling link.239/// one; the latter case is known as a dangling link.
...@@ -287,222 +271,6 @@ pub fn symLinkAbsoluteW(...@@ -287,222 +271,6 @@ pub fn symLinkAbsoluteW(
287 return windows.CreateSymbolicLink(null, mem.span(sym_link_path_w), mem.span(target_path_w), flags.is_directory);271 return windows.CreateSymbolicLink(null, mem.span(sym_link_path_w), mem.span(target_path_w), flags.is_directory);
288}272}
289273
290// This is `posix.ReadLinkError || posix.RealPathError` with impossible errors excluded
291pub const SelfExePathError = error{
292 FileNotFound,
293 AccessDenied,
294 NameTooLong,
295 NotSupported,
296 NotDir,
297 SymLinkLoop,
298 InputOutput,
299 FileTooBig,
300 IsDir,
301 ProcessFdQuotaExceeded,
302 SystemFdQuotaExceeded,
303 NoDevice,
304 SystemResources,
305 NoSpaceLeft,
306 FileSystem,
307 BadPathName,
308 DeviceBusy,
309 SharingViolation,
310 PipeBusy,
311 NotLink,
312 PathAlreadyExists,
313
314 /// On Windows, `\\server` or `\\server\share` was not found.
315 NetworkNotFound,
316 ProcessNotFound,
317
318 /// On Windows, antivirus software is enabled by default. It can be
319 /// disabled, but Windows Update sometimes ignores the user's preference
320 /// and re-enables it. When enabled, antivirus software on Windows
321 /// intercepts file system operations and makes them significantly slower
322 /// in addition to possibly failing with this error code.
323 AntivirusInterference,
324
325 /// On Windows, the volume does not contain a recognized file system. File
326 /// system drivers might not be loaded, or the volume may be corrupt.
327 UnrecognizedVolume,
328
329 Canceled,
330} || posix.SysCtlError;
331
332/// `selfExePath` except allocates the result on the heap.
333/// Caller owns returned memory.
334pub fn selfExePathAlloc(allocator: Allocator) ![]u8 {
335 // Use of max_path_bytes here is justified as, at least on one tested Linux
336 // system, readlink will completely fail to return a result larger than
337 // PATH_MAX even if given a sufficiently large buffer. This makes it
338 // fundamentally impossible to get the selfExePath of a program running in
339 // a very deeply nested directory chain in this way.
340 // TODO(#4812): Investigate other systems and whether it is possible to get
341 // this path by trying larger and larger buffers until one succeeds.
342 var buf: [max_path_bytes]u8 = undefined;
343 return allocator.dupe(u8, try selfExePath(&buf));
344}
345
346/// Get the path to the current executable. Follows symlinks.
347/// If you only need the directory, use selfExeDirPath.
348/// If you only want an open file handle, use openSelfExe.
349/// This function may return an error if the current executable
350/// was deleted after spawning.
351/// Returned value is a slice of out_buffer.
352/// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
353/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
354///
355/// On Linux, depends on procfs being mounted. If the currently executing binary has
356/// been deleted, the file path looks something like `/a/b/c/exe (deleted)`.
357/// TODO make the return type of this a null terminated pointer
358pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
359 if (is_darwin) {
360 // Note that _NSGetExecutablePath() will return "a path" to
361 // the executable not a "real path" to the executable.
362 var symlink_path_buf: [max_path_bytes:0]u8 = undefined;
363 var u32_len: u32 = max_path_bytes + 1; // include the sentinel
364 const rc = std.c._NSGetExecutablePath(&symlink_path_buf, &u32_len);
365 if (rc != 0) return error.NameTooLong;
366
367 var real_path_buf: [max_path_bytes]u8 = undefined;
368 const real_path = std.posix.realpathZ(&symlink_path_buf, &real_path_buf) catch |err| switch (err) {
369 error.NetworkNotFound => unreachable, // Windows-only
370 else => |e| return e,
371 };
372 if (real_path.len > out_buffer.len) return error.NameTooLong;
373 const result = out_buffer[0..real_path.len];
374 @memcpy(result, real_path);
375 return result;
376 }
377 switch (native_os) {
378 .linux, .serenity => return posix.readlinkZ("/proc/self/exe", out_buffer) catch |err| switch (err) {
379 error.UnsupportedReparsePointType => unreachable, // Windows-only
380 error.NetworkNotFound => unreachable, // Windows-only
381 else => |e| return e,
382 },
383 .illumos => return posix.readlinkZ("/proc/self/path/a.out", out_buffer) catch |err| switch (err) {
384 error.UnsupportedReparsePointType => unreachable, // Windows-only
385 error.NetworkNotFound => unreachable, // Windows-only
386 else => |e| return e,
387 },
388 .freebsd, .dragonfly => {
389 var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC, posix.KERN.PROC_PATHNAME, -1 };
390 var out_len: usize = out_buffer.len;
391 try posix.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
392 // TODO could this slice from 0 to out_len instead?
393 return mem.sliceTo(out_buffer, 0);
394 },
395 .netbsd => {
396 var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC_ARGS, -1, posix.KERN.PROC_PATHNAME };
397 var out_len: usize = out_buffer.len;
398 try posix.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
399 // TODO could this slice from 0 to out_len instead?
400 return mem.sliceTo(out_buffer, 0);
401 },
402 .openbsd, .haiku => {
403 // OpenBSD doesn't support getting the path of a running process, so try to guess it
404 if (std.os.argv.len == 0)
405 return error.FileNotFound;
406
407 const argv0 = mem.span(std.os.argv[0]);
408 if (mem.find(u8, argv0, "/") != null) {
409 // argv[0] is a path (relative or absolute): use realpath(3) directly
410 var real_path_buf: [max_path_bytes]u8 = undefined;
411 const real_path = posix.realpathZ(std.os.argv[0], &real_path_buf) catch |err| switch (err) {
412 error.NetworkNotFound => unreachable, // Windows-only
413 else => |e| return e,
414 };
415 if (real_path.len > out_buffer.len)
416 return error.NameTooLong;
417 const result = out_buffer[0..real_path.len];
418 @memcpy(result, real_path);
419 return result;
420 } else if (argv0.len != 0) {
421 // argv[0] is not empty (and not a path): search it inside PATH
422 const PATH = posix.getenvZ("PATH") orelse return error.FileNotFound;
423 var path_it = mem.tokenizeScalar(u8, PATH, path.delimiter);
424 while (path_it.next()) |a_path| {
425 var resolved_path_buf: [max_path_bytes - 1:0]u8 = undefined;
426 const resolved_path = std.fmt.bufPrintSentinel(&resolved_path_buf, "{s}/{s}", .{
427 a_path,
428 std.os.argv[0],
429 }, 0) catch continue;
430
431 var real_path_buf: [max_path_bytes]u8 = undefined;
432 if (posix.realpathZ(resolved_path, &real_path_buf)) |real_path| {
433 // found a file, and hope it is the right file
434 if (real_path.len > out_buffer.len)
435 return error.NameTooLong;
436 const result = out_buffer[0..real_path.len];
437 @memcpy(result, real_path);
438 return result;
439 } else |_| continue;
440 }
441 }
442 return error.FileNotFound;
443 },
444 .windows => {
445 const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
446 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
447
448 // If ImagePathName is a symlink, then it will contain the path of the
449 // symlink, not the path that the symlink points to. We want the path
450 // that the symlink points to, though, so we need to get the realpath.
451 var pathname_w = try windows.wToPrefixedFileW(null, image_path_name);
452
453 const wide_slice = try std.fs.cwd().realpathW2(pathname_w.span(), &pathname_w.data);
454
455 const len = std.unicode.calcWtf8Len(wide_slice);
456 if (len > out_buffer.len)
457 return error.NameTooLong;
458
459 const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
460 return out_buffer[0..end_index];
461 },
462 else => @compileError("std.fs.selfExePath not supported for this target"),
463 }
464}
465
466/// `selfExeDirPath` except allocates the result on the heap.
467/// Caller owns returned memory.
468pub fn selfExeDirPathAlloc(allocator: Allocator) ![]u8 {
469 // Use of max_path_bytes here is justified as, at least on one tested Linux
470 // system, readlink will completely fail to return a result larger than
471 // PATH_MAX even if given a sufficiently large buffer. This makes it
472 // fundamentally impossible to get the selfExeDirPath of a program running
473 // in a very deeply nested directory chain in this way.
474 // TODO(#4812): Investigate other systems and whether it is possible to get
475 // this path by trying larger and larger buffers until one succeeds.
476 var buf: [max_path_bytes]u8 = undefined;
477 return allocator.dupe(u8, try selfExeDirPath(&buf));
478}
479
480/// Get the directory path that contains the current executable.
481/// Returned value is a slice of out_buffer.
482/// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
483/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
484pub fn selfExeDirPath(out_buffer: []u8) SelfExePathError![]const u8 {
485 const self_exe_path = try selfExePath(out_buffer);
486 // Assume that the OS APIs return absolute paths, and therefore dirname
487 // will not return null.
488 return path.dirname(self_exe_path).?;
489}
490
491/// `realpath`, except caller must free the returned memory.
492/// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
493/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
494/// See also `Dir.realpath`.
495pub fn realpathAlloc(allocator: Allocator, pathname: []const u8) ![]u8 {
496 // Use of max_path_bytes here is valid as the realpath function does not
497 // have a variant that takes an arbitrary-size buffer.
498 // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008
499 // NULL out parameter (GNU's canonicalize_file_name) to handle overelong
500 // paths. musl supports passing NULL but restricts the output to PATH_MAX
501 // anyway.
502 var buf: [max_path_bytes]u8 = undefined;
503 return allocator.dupe(u8, try posix.realpath(pathname, &buf));
504}
505
506test {274test {
507 _ = AtomicFile;275 _ = AtomicFile;
508 _ = Dir;276 _ = Dir;
lib/std/fs/test.zig+50-5
...@@ -4,6 +4,7 @@ const native_os = builtin.os.tag;...@@ -4,6 +4,7 @@ const native_os = builtin.os.tag;
4const std = @import("../std.zig");4const std = @import("../std.zig");
5const Io = std.Io;5const Io = std.Io;
6const testing = std.testing;6const testing = std.testing;
7const expect = std.testing.expect;
7const fs = std.fs;8const fs = std.fs;
8const mem = std.mem;9const mem = std.mem;
9const wasi = std.os.wasi;10const wasi = std.os.wasi;
...@@ -1177,21 +1178,22 @@ test "renameAbsolute" {...@@ -1177,21 +1178,22 @@ test "renameAbsolute" {
1177 dir.close(io);1178 dir.close(io);
1178}1179}
11791180
1180test "openSelfExe" {1181test "openExecutable" {
1181 if (native_os == .wasi) return error.SkipZigTest;1182 if (native_os == .wasi) return error.SkipZigTest;
11821183
1183 const io = testing.io;1184 const io = testing.io;
11841185
1185 const self_exe_file = try std.fs.openSelfExe(.{});1186 const self_exe_file = try std.fs.openExecutable(.{});
1186 self_exe_file.close(io);1187 self_exe_file.close(io);
1187}1188}
11881189
1189test "selfExePath" {1190test "executablePath" {
1190 if (native_os == .wasi) return error.SkipZigTest;1191 if (native_os == .wasi) return error.SkipZigTest;
11911192
1193 const io = testing.io;
1192 var buf: [fs.max_path_bytes]u8 = undefined;1194 var buf: [fs.max_path_bytes]u8 = undefined;
1193 const buf_self_exe_path = try std.fs.selfExePath(&buf);1195 const buf_self_exe_path = try std.process.executablePath(io, &buf);
1194 const alloc_self_exe_path = try std.fs.selfExePathAlloc(testing.allocator);1196 const alloc_self_exe_path = try std.process.executablePathAlloc(io, testing.allocator);
1195 defer testing.allocator.free(alloc_self_exe_path);1197 defer testing.allocator.free(alloc_self_exe_path);
1196 try testing.expectEqualSlices(u8, buf_self_exe_path, alloc_self_exe_path);1198 try testing.expectEqualSlices(u8, buf_self_exe_path, alloc_self_exe_path);
1197}1199}
...@@ -2371,3 +2373,46 @@ test "File.Writer sendfile with buffered contents" {...@@ -2371,3 +2373,46 @@ test "File.Writer sendfile with buffered contents" {
2371 try testing.expectEqualStrings("abcd", try check_r.interface.take(4));2373 try testing.expectEqualStrings("abcd", try check_r.interface.take(4));
2372 try testing.expectError(error.EndOfStream, check_r.interface.takeByte());2374 try testing.expectError(error.EndOfStream, check_r.interface.takeByte());
2373}2375}
2376
2377test "readlink on Windows" {
2378 if (native_os != .windows) return error.SkipZigTest;
2379
2380 try testReadlink("C:\\ProgramData", "C:\\Users\\All Users");
2381 try testReadlink("C:\\Users\\Default", "C:\\Users\\Default User");
2382 try testReadlink("C:\\Users", "C:\\Documents and Settings");
2383}
2384
2385fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void {
2386 var buffer: [fs.max_path_bytes]u8 = undefined;
2387 const given = try Dir.readLinkAbsolute(symlink_path, buffer[0..]);
2388 try expect(mem.eql(u8, target_path, given));
2389}
2390
2391test "readlinkat" {
2392 var tmp = tmpDir(.{});
2393 defer tmp.cleanup();
2394
2395 // create file
2396 try tmp.dir.writeFile(.{ .sub_path = "file.txt", .data = "nonsense" });
2397
2398 // create a symbolic link
2399 if (native_os == .windows) {
2400 std.os.windows.CreateSymbolicLink(
2401 tmp.dir.fd,
2402 &[_]u16{ 'l', 'i', 'n', 'k' },
2403 &[_:0]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' },
2404 false,
2405 ) catch |err| switch (err) {
2406 // Symlink requires admin privileges on windows, so this test can legitimately fail.
2407 error.AccessDenied => return error.SkipZigTest,
2408 else => return err,
2409 };
2410 } else {
2411 try posix.symlinkat("file.txt", tmp.dir.fd, "link");
2412 }
2413
2414 // read the link
2415 var buffer: [fs.max_path_bytes]u8 = undefined;
2416 const read_link = try tmp.dir.readLink("link", &buffer);
2417 try expect(mem.eql(u8, "file.txt", read_link));
2418}
lib/std/posix/test.zig-43
...@@ -111,20 +111,6 @@ test "open smoke test" {...@@ -111,20 +111,6 @@ test "open smoke test" {
111 }111 }
112}112}
113113
114test "readlink on Windows" {
115 if (native_os != .windows) return error.SkipZigTest;
116
117 try testReadlink("C:\\ProgramData", "C:\\Users\\All Users");
118 try testReadlink("C:\\Users\\Default", "C:\\Users\\Default User");
119 try testReadlink("C:\\Users", "C:\\Documents and Settings");
120}
121
122fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void {
123 var buffer: [fs.max_path_bytes]u8 = undefined;
124 const given = try posix.readlink(symlink_path, buffer[0..]);
125 try expect(mem.eql(u8, target_path, given));
126}
127
128fn getLinkInfo(fd: posix.fd_t) !struct { posix.ino_t, posix.nlink_t } {114fn getLinkInfo(fd: posix.fd_t) !struct { posix.ino_t, posix.nlink_t } {
129 if (native_os == .linux) {115 if (native_os == .linux) {
130 const stx = try linux.wrapped.statx(116 const stx = try linux.wrapped.statx(
...@@ -216,35 +202,6 @@ test "fstatat" {...@@ -216,35 +202,6 @@ test "fstatat" {
216 // try expectEqual(stat.blocks, statat.blocks);202 // try expectEqual(stat.blocks, statat.blocks);
217}203}
218204
219test "readlinkat" {
220 var tmp = tmpDir(.{});
221 defer tmp.cleanup();
222
223 // create file
224 try tmp.dir.writeFile(.{ .sub_path = "file.txt", .data = "nonsense" });
225
226 // create a symbolic link
227 if (native_os == .windows) {
228 std.os.windows.CreateSymbolicLink(
229 tmp.dir.fd,
230 &[_]u16{ 'l', 'i', 'n', 'k' },
231 &[_:0]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' },
232 false,
233 ) catch |err| switch (err) {
234 // Symlink requires admin privileges on windows, so this test can legitimately fail.
235 error.AccessDenied => return error.SkipZigTest,
236 else => return err,
237 };
238 } else {
239 try posix.symlinkat("file.txt", tmp.dir.fd, "link");
240 }
241
242 // read the link
243 var buffer: [fs.max_path_bytes]u8 = undefined;
244 const read_link = try posix.readlinkat(tmp.dir.fd, "link", buffer[0..]);
245 try expect(mem.eql(u8, "file.txt", read_link));
246}
247
248test "getrandom" {205test "getrandom" {
249 var buf_a: [50]u8 = undefined;206 var buf_a: [50]u8 = undefined;
250 var buf_b: [50]u8 = undefined;207 var buf_b: [50]u8 = undefined;
lib/std/process.zig+97-1
...@@ -3,6 +3,7 @@ const native_os = builtin.os.tag;...@@ -3,6 +3,7 @@ const native_os = builtin.os.tag;
33
4const std = @import("std.zig");4const std = @import("std.zig");
5const Io = std.Io;5const Io = std.Io;
6const File = std.Io.File;
6const fs = std.fs;7const fs = std.fs;
7const mem = std.mem;8const mem = std.mem;
8const math = std.math;9const math = std.math;
...@@ -12,6 +13,7 @@ const testing = std.testing;...@@ -12,6 +13,7 @@ const testing = std.testing;
12const posix = std.posix;13const posix = std.posix;
13const windows = std.os.windows;14const windows = std.os.windows;
14const unicode = std.unicode;15const unicode = std.unicode;
16const max_path_bytes = std.fs.max_path_bytes;
1517
16pub const Child = @import("process/Child.zig");18pub const Child = @import("process/Child.zig");
17pub const abort = posix.abort;19pub const abort = posix.abort;
...@@ -37,7 +39,7 @@ pub const GetCwdAllocError = Allocator.Error || error{CurrentWorkingDirectoryUnl...@@ -37,7 +39,7 @@ pub const GetCwdAllocError = Allocator.Error || error{CurrentWorkingDirectoryUnl
37pub fn getCwdAlloc(allocator: Allocator) GetCwdAllocError![]u8 {39pub fn getCwdAlloc(allocator: Allocator) GetCwdAllocError![]u8 {
38 // The use of max_path_bytes here is just a heuristic: most paths will fit40 // The use of max_path_bytes here is just a heuristic: most paths will fit
39 // in stack_buf, avoiding an extra allocation in the common case.41 // in stack_buf, avoiding an extra allocation in the common case.
40 var stack_buf: [fs.max_path_bytes]u8 = undefined;42 var stack_buf: [max_path_bytes]u8 = undefined;
41 var heap_buf: ?[]u8 = null;43 var heap_buf: ?[]u8 = null;
42 defer if (heap_buf) |buf| allocator.free(buf);44 defer if (heap_buf) |buf| allocator.free(buf);
4345
...@@ -2112,3 +2114,97 @@ pub fn fatal(comptime format: []const u8, format_arguments: anytype) noreturn {...@@ -2112,3 +2114,97 @@ pub fn fatal(comptime format: []const u8, format_arguments: anytype) noreturn {
2112 std.log.err(format, format_arguments);2114 std.log.err(format, format_arguments);
2113 exit(1);2115 exit(1);
2114}2116}
2117
2118pub const ExecutablePathBaseError = error{
2119 FileNotFound,
2120 AccessDenied,
2121 NotSupported,
2122 NotDir,
2123 SymLinkLoop,
2124 InputOutput,
2125 FileTooBig,
2126 IsDir,
2127 ProcessFdQuotaExceeded,
2128 SystemFdQuotaExceeded,
2129 NoDevice,
2130 SystemResources,
2131 NoSpaceLeft,
2132 FileSystem,
2133 BadPathName,
2134 DeviceBusy,
2135 SharingViolation,
2136 PipeBusy,
2137 NotLink,
2138 PathAlreadyExists,
2139 /// On Windows, `\\server` or `\\server\share` was not found.
2140 NetworkNotFound,
2141 ProcessNotFound,
2142 /// On Windows, antivirus software is enabled by default. It can be
2143 /// disabled, but Windows Update sometimes ignores the user's preference
2144 /// and re-enables it. When enabled, antivirus software on Windows
2145 /// intercepts file system operations and makes them significantly slower
2146 /// in addition to possibly failing with this error code.
2147 AntivirusInterference,
2148 /// On Windows, the volume does not contain a recognized file system. File
2149 /// system drivers might not be loaded, or the volume may be corrupt.
2150 UnrecognizedVolume,
2151 PermissionDenied,
2152} || Io.Cancelable || Io.UnexpectedError;
2153
2154pub const ExecutablePathAllocError = ExecutablePathBaseError || Allocator.Error;
2155
2156pub fn executablePathAlloc(io: Io, allocator: Allocator) ExecutablePathAllocError![:0]u8 {
2157 var buffer: [max_path_bytes]u8 = undefined;
2158 const n = executablePath(io, &buffer) catch |err| switch (err) {
2159 error.NameTooLong => unreachable,
2160 else => |e| return e,
2161 };
2162 return allocator.dupeZ(u8, buffer[0..n]);
2163}
2164
2165pub const ExecutablePathError = ExecutablePathBaseError || error{NameTooLong};
2166
2167/// Get the path to the current executable, following symlinks.
2168///
2169/// This function may return an error if the current executable
2170/// was deleted after spawning.
2171///
2172/// Returned value is a slice of out_buffer.
2173///
2174/// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
2175/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
2176///
2177/// On Linux, depends on procfs being mounted. If the currently executing binary has
2178/// been deleted, the file path looks something like "/a/b/c/exe (deleted)".
2179///
2180/// See also:
2181/// * `executableDirPath` - to obtain only the directory
2182/// * `openExecutable` - to obtain only an open file handle
2183pub fn executablePath(io: Io, out_buffer: []u8) ExecutablePathError!usize {
2184 return io.vtable.processExecutablePath(io.userdata, out_buffer);
2185}
2186
2187/// Get the directory path that contains the current executable.
2188///
2189/// Returns index into `out_buffer`.
2190///
2191/// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
2192/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
2193pub fn executableDirPath(out_buffer: []u8) ExecutablePathError!usize {
2194 const n = try executablePath(out_buffer);
2195 // Assert that the OS APIs return absolute paths, and therefore dirname
2196 // will not return null.
2197 return std.fs.path.dirname(out_buffer[0..n]).?;
2198}
2199
2200/// Same as `executableDirPath` except allocates the result.
2201pub fn executableDirPathAlloc(allocator: Allocator) ![]u8 {
2202 var buffer: [max_path_bytes]u8 = undefined;
2203 return allocator.dupe(u8, try executableDirPath(&buffer));
2204}
2205
2206pub const OpenExecutableError = File.OpenError || ExecutablePathError || File.LockError;
2207
2208pub fn openExecutable(io: Io, flags: File.OpenFlags) OpenExecutableError!File {
2209 return io.vtable.processExecutableOpen(io.userdata, flags);
2210}
lib/std/zig/system.zig+4-7
...@@ -209,7 +209,6 @@ pub const DetectError = error{...@@ -209,7 +209,6 @@ pub const DetectError = error{
209 DeviceBusy,209 DeviceBusy,
210 OSVersionDetectionFail,210 OSVersionDetectionFail,
211 Unexpected,211 Unexpected,
212 ProcessNotFound,
213} || Io.Cancelable;212} || Io.Cancelable;
214213
215/// Given a `Target.Query`, which specifies in detail which parts of the214/// Given a `Target.Query`, which specifies in detail which parts of the
...@@ -422,7 +421,6 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {...@@ -422,7 +421,6 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {
422 error.SocketUnconnected => return error.Unexpected,421 error.SocketUnconnected => return error.Unexpected,
423422
424 error.AccessDenied,423 error.AccessDenied,
425 error.ProcessNotFound,
426 error.SymLinkLoop,424 error.SymLinkLoop,
427 error.ProcessFdQuotaExceeded,425 error.ProcessFdQuotaExceeded,
428 error.SystemFdQuotaExceeded,426 error.SystemFdQuotaExceeded,
...@@ -553,7 +551,6 @@ pub const AbiAndDynamicLinkerFromFileError = error{...@@ -553,7 +551,6 @@ pub const AbiAndDynamicLinkerFromFileError = error{
553 SystemResources,551 SystemResources,
554 ProcessFdQuotaExceeded,552 ProcessFdQuotaExceeded,
555 SystemFdQuotaExceeded,553 SystemFdQuotaExceeded,
556 ProcessNotFound,
557 IsDir,554 IsDir,
558 WouldBlock,555 WouldBlock,
559 InputOutput,556 InputOutput,
...@@ -693,8 +690,10 @@ fn abiAndDynamicLinkerFromFile(...@@ -693,8 +690,10 @@ fn abiAndDynamicLinkerFromFile(
693690
694 // So far, no luck. Next we try to see if the information is691 // So far, no luck. Next we try to see if the information is
695 // present in the symlink data for the dynamic linker path.692 // present in the symlink data for the dynamic linker path.
696 var link_buf: [posix.PATH_MAX]u8 = undefined;693 var link_buffer: [posix.PATH_MAX]u8 = undefined;
697 const link_name = posix.readlink(dl_path, &link_buf) catch |err| switch (err) {694 const link_name = if (Io.Dir.readLinkAbsolute(io, dl_path, &link_buffer)) |n|
695 link_buffer[0..n]
696 else |err| switch (err) {
698 error.NameTooLong => unreachable,697 error.NameTooLong => unreachable,
699 error.BadPathName => unreachable, // Windows only698 error.BadPathName => unreachable, // Windows only
700 error.UnsupportedReparsePointType => unreachable, // Windows only699 error.UnsupportedReparsePointType => unreachable, // Windows only
...@@ -839,7 +838,6 @@ fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion {...@@ -839,7 +838,6 @@ fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion {
839 error.NotDir => return error.GLibCNotFound,838 error.NotDir => return error.GLibCNotFound,
840 error.IsDir => return error.GLibCNotFound,839 error.IsDir => return error.GLibCNotFound,
841840
842 error.ProcessNotFound => |e| return e,
843 error.ProcessFdQuotaExceeded => |e| return e,841 error.ProcessFdQuotaExceeded => |e| return e,
844 error.SystemFdQuotaExceeded => |e| return e,842 error.SystemFdQuotaExceeded => |e| return e,
845 error.SystemResources => |e| return e,843 error.SystemResources => |e| return e,
...@@ -1103,7 +1101,6 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ...@@ -1103,7 +1101,6 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
1103 error.SymLinkLoop,1101 error.SymLinkLoop,
1104 error.ProcessFdQuotaExceeded,1102 error.ProcessFdQuotaExceeded,
1105 error.SystemFdQuotaExceeded,1103 error.SystemFdQuotaExceeded,
1106 error.ProcessNotFound,
1107 error.Canceled,1104 error.Canceled,
1108 => |e| return e,1105 => |e| return e,
11091106