| author | |
| committer | |
| log | 877032ec6a0007316f42658d12042f1473de4856 |
| tree | d6541c2050b24f97ef80895061e881bc31fc5449 |
| parent | 8328de24f13e21e325207b19288a143854df50df |
12 files changed, 333 insertions(+), 315 deletions(-)
lib/std/Io.zig+3-1| ... | ... | @@ -697,7 +697,6 @@ pub const VTable = struct { |
| 697 | 697 | fileReadPositional: *const fn (?*anyopaque, File, data: [][]u8, offset: u64) File.ReadPositionalError!usize, |
| 698 | 698 | fileSeekBy: *const fn (?*anyopaque, File, relative_offset: i64) File.SeekError!void, |
| 699 | 699 | fileSeekTo: *const fn (?*anyopaque, File, absolute_offset: u64) File.SeekError!void, |
| 700 | openSelfExe: *const fn (?*anyopaque, File.OpenFlags) File.OpenSelfExeError!File, | |
| 701 | 700 | fileSync: *const fn (?*anyopaque, File) File.SyncError!void, |
| 702 | 701 | fileIsTty: *const fn (?*anyopaque, File) Cancelable!bool, |
| 703 | 702 | fileEnableAnsiEscapeCodes: *const fn (?*anyopaque, File) File.EnableAnsiEscapeCodesError!void, |
| ... | ... | @@ -712,6 +711,9 @@ pub const VTable = struct { |
| 712 | 711 | fileUnlock: *const fn (?*anyopaque, File) void, |
| 713 | 712 | fileDowngradeLock: *const fn (?*anyopaque, File) File.DowngradeLockError!void, |
| 714 | 713 | |
| 714 | processExecutableOpen: *const fn (?*anyopaque, File.OpenFlags) std.process.OpenExecutableError!File, | |
| 715 | processExecutablePath: *const fn (?*anyopaque, buffer: []u8) std.process.ExecutablePathError!usize, | |
| 716 | ||
| 715 | 717 | now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, |
| 716 | 718 | sleep: *const fn (?*anyopaque, Timeout) SleepError!void, |
| 717 | 719 |
lib/std/Io/Dir.zig+27-12| ... | ... | @@ -694,24 +694,29 @@ pub const RealPathError = error{ |
| 694 | 694 | /// supported hosts are: Linux, macOS, and Windows. |
| 695 | 695 | /// |
| 696 | 696 | /// See also: |
| 697 | /// * `realpathAlloc`. | |
| 697 | /// * `realPathAlloc`. | |
| 698 | 698 | pub fn realPath(dir: Dir, io: Io, sub_path: []const u8, out_buffer: []u8) RealPathError!usize { |
| 699 | 699 | return io.vtable.dirRealPath(io.userdata, dir, sub_path, out_buffer); |
| 700 | 700 | } |
| 701 | 701 | |
| 702 | 702 | pub const RealPathAllocError = RealPathError || Allocator.Error; |
| 703 | 703 | |
| 704 | /// Same as `Dir.realpath` except caller must free the returned memory. | |
| 705 | /// See also `Dir.realpath`. | |
| 706 | pub fn realpathAlloc(self: Dir, allocator: Allocator, pathname: []const u8) RealPathAllocError![]u8 { | |
| 707 | // Use of max_path_bytes here is valid as the realpath function does not | |
| 708 | // have a variant that takes an arbitrary-size buffer. | |
| 709 | // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008 | |
| 710 | // NULL out parameter (GNU's canonicalize_file_name) to handle overelong | |
| 711 | // paths. musl supports passing NULL but restricts the output to PATH_MAX | |
| 712 | // anyway. | |
| 713 | var buf: [std.fs.max_path_bytes]u8 = undefined; | |
| 714 | return allocator.dupe(u8, try self.realpath(pathname, &buf)); | |
| 704 | /// Same as `realPath` except allocates result. | |
| 705 | pub fn realPathAlloc(dir: Dir, io: Io, sub_path: []const u8, allocator: Allocator) RealPathAllocError![:0]u8 { | |
| 706 | var buffer: [std.fs.max_path_bytes]u8 = undefined; | |
| 707 | const n = try realPath(dir, io, sub_path, &buffer); | |
| 708 | return allocator.dupeZ(u8, buffer[0..n]); | |
| 709 | } | |
| 710 | ||
| 711 | pub fn realPathAbsolute(io: Io, path: []const u8, out_buffer: []u8) RealPathError!usize { | |
| 712 | return io.vtable.dirRealPath(io.userdata, .cwd(), path, out_buffer); | |
| 713 | } | |
| 714 | ||
| 715 | /// Same as `realPathAbsolute` except allocates result. | |
| 716 | pub 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 | } |
| 716 | 721 | |
| 717 | 722 | pub const DeleteFileError = error{ |
| ... | ... | @@ -975,6 +980,16 @@ pub fn readLink(dir: Dir, io: Io, sub_path: []const u8, buffer: []u8) ReadLinkEr |
| 975 | 980 | return io.vtable.dirReadLink(io.userdata, dir, sub_path, buffer); |
| 976 | 981 | } |
| 977 | 982 | |
| 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. | |
| 988 | pub 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 | ||
| 978 | 993 | pub const ReadFileAllocError = File.OpenError || File.ReadError || Allocator.Error || error{ |
| 979 | 994 | /// File size reached or exceeded the provided limit. |
| 980 | 995 | StreamTooLong, |
lib/std/Io/File.zig-6| ... | ... | @@ -464,12 +464,6 @@ pub fn setTimestampsNow(file: File, io: Io) SetTimestampsError!void { |
| 464 | 464 | return io.vtable.fileSetTimestampsNow(io.userdata, file); |
| 465 | 465 | } |
| 466 | 466 | |
| 467 | pub const OpenSelfExeError = OpenError || std.fs.SelfExePathError || LockError; | |
| 468 | ||
| 469 | pub fn openSelfExe(io: Io, flags: OpenFlags) OpenSelfExeError!File { | |
| 470 | return io.vtable.openSelfExe(io.userdata, flags); | |
| 471 | } | |
| 472 | ||
| 473 | 467 | pub const ReadPositionalError = Reader.Error || error{Unseekable}; |
| 474 | 468 | |
| 475 | 469 | pub 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 | 888 | .fileReadPositional = fileReadPositional, |
| 889 | 889 | .fileSeekBy = fileSeekBy, |
| 890 | 890 | .fileSeekTo = fileSeekTo, |
| 891 | .openSelfExe = openSelfExe, | |
| 891 | .openExecutable = openExecutable, | |
| 892 | 892 | |
| 893 | 893 | .now = now, |
| 894 | 894 | .sleep = sleep, |
| ... | ... | @@ -1246,7 +1246,7 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, absolute_offset: u64) File.Seek |
| 1246 | 1246 | _ = absolute_offset; |
| 1247 | 1247 | @panic("TODO"); |
| 1248 | 1248 | } |
| 1249 | fn openSelfExe(userdata: ?*anyopaque, file: File.OpenFlags) File.OpenSelfExeError!File { | |
| 1249 | fn openExecutable(userdata: ?*anyopaque, file: File.OpenFlags) File.OpenExecutableError!File { | |
| 1250 | 1250 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1251 | 1251 | _ = k; |
| 1252 | 1252 | _ = file; |
lib/std/Io/Threaded.zig+148-4| ... | ... | @@ -717,7 +717,6 @@ pub fn io(t: *Threaded) Io { |
| 717 | 717 | .fileReadPositional = fileReadPositional, |
| 718 | 718 | .fileSeekBy = fileSeekBy, |
| 719 | 719 | .fileSeekTo = fileSeekTo, |
| 720 | .openSelfExe = openSelfExe, | |
| 721 | 720 | .fileSync = fileSync, |
| 722 | 721 | .fileIsTty = fileIsTty, |
| 723 | 722 | .fileEnableAnsiEscapeCodes = fileEnableAnsiEscapeCodes, |
| ... | ... | @@ -732,6 +731,9 @@ pub fn io(t: *Threaded) Io { |
| 732 | 731 | .fileUnlock = fileUnlock, |
| 733 | 732 | .fileDowngradeLock = fileDowngradeLock, |
| 734 | 733 | |
| 734 | .processExecutableOpen = processExecutableOpen, | |
| 735 | .processExecutablePath = processExecutablePath, | |
| 736 | ||
| 735 | 737 | .now = now, |
| 736 | 738 | .sleep = sleep, |
| 737 | 739 | |
| ... | ... | @@ -839,7 +841,6 @@ pub fn ioBasic(t: *Threaded) Io { |
| 839 | 841 | .fileReadPositional = fileReadPositional, |
| 840 | 842 | .fileSeekBy = fileSeekBy, |
| 841 | 843 | .fileSeekTo = fileSeekTo, |
| 842 | .openSelfExe = openSelfExe, | |
| 843 | 844 | .fileSync = fileSync, |
| 844 | 845 | .fileIsTty = fileIsTty, |
| 845 | 846 | .fileEnableAnsiEscapeCodes = fileEnableAnsiEscapeCodes, |
| ... | ... | @@ -854,6 +855,9 @@ pub fn ioBasic(t: *Threaded) Io { |
| 854 | 855 | .fileUnlock = fileUnlock, |
| 855 | 856 | .fileDowngradeLock = fileDowngradeLock, |
| 856 | 857 | |
| 858 | .processExecutableOpen = processExecutableOpen, | |
| 859 | .processExecutablePath = processExecutablePath, | |
| 860 | ||
| 857 | 861 | .now = now, |
| 858 | 862 | .sleep = sleep, |
| 859 | 863 | |
| ... | ... | @@ -5932,7 +5936,7 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!voi |
| 5932 | 5936 | } |
| 5933 | 5937 | } |
| 5934 | 5938 | |
| 5935 | fn openSelfExe(userdata: ?*anyopaque, flags: File.OpenFlags) File.OpenSelfExeError!File { | |
| 5939 | fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) std.process.OpenExecutableError!File { | |
| 5936 | 5940 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 5937 | 5941 | switch (native_os) { |
| 5938 | 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 | 5949 | const prefixed_path_w = try windows.wToPrefixedFileW(null, image_path_name); |
| 5946 | 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 | ||
| 5956 | fn 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 | } |
| 5951 | 6095 |
lib/std/debug/SelfInfo/Elf.zig+1-1| ... | ... | @@ -329,7 +329,7 @@ const Module = struct { |
| 329 | 329 | defer file.close(io); |
| 330 | 330 | break :res std.debug.ElfFile.load(gpa, file, mod.build_id, &.native(mod.name)); |
| 331 | 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 | 333 | error.OutOfMemory => |e| return e, |
| 334 | 334 | else => return error.ReadFailed, |
| 335 | 335 | }; |
lib/std/debug/SelfInfo/Windows.zig+1-1| ... | ... | @@ -434,7 +434,7 @@ const Module = struct { |
| 434 | 434 | const pdb_file_open_result = if (fs.path.isAbsolute(path)) res: { |
| 435 | 435 | break :res std.fs.cwd().openFile(io, path, .{}); |
| 436 | 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 | 438 | error.OutOfMemory, error.Unexpected => |e| return e, |
| 439 | 439 | else => return error.ReadFailed, |
| 440 | 440 | }; |
lib/std/fs.zig-232| ... | ... | @@ -25,13 +25,6 @@ pub const File = std.Io.File; |
| 25 | 25 | pub const path = @import("fs/path.zig"); |
| 26 | 26 | pub const wasi = @import("fs/wasi.zig"); |
| 27 | 27 | |
| 28 | // TODO audit these APIs with respect to Dir and absolute paths | |
| 29 | ||
| 30 | pub const realpath = posix.realpath; | |
| 31 | pub const realpathZ = posix.realpathZ; | |
| 32 | pub const realpathW = posix.realpathW; | |
| 33 | pub const realpathW2 = posix.realpathW2; | |
| 34 | ||
| 35 | 28 | pub const getAppDataDir = @import("fs/get_app_data_dir.zig").getAppDataDir; |
| 36 | 29 | pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirError; |
| 37 | 30 | |
| ... | ... | @@ -241,15 +234,6 @@ pub fn deleteTreeAbsolute(io: Io, absolute_path: []const u8) !void { |
| 241 | 234 | return dir.deleteTree(path.basename(absolute_path)); |
| 242 | 235 | } |
| 243 | 236 | |
| 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. | |
| 248 | pub 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 | 237 | /// Creates a symbolic link named `sym_link_path` which contains the string `target_path`. |
| 254 | 238 | /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent |
| 255 | 239 | /// one; the latter case is known as a dangling link. |
| ... | ... | @@ -287,222 +271,6 @@ pub fn symLinkAbsoluteW( |
| 287 | 271 | return windows.CreateSymbolicLink(null, mem.span(sym_link_path_w), mem.span(target_path_w), flags.is_directory); |
| 288 | 272 | } |
| 289 | 273 | |
| 290 | // This is `posix.ReadLinkError || posix.RealPathError` with impossible errors excluded | |
| 291 | pub 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. | |
| 334 | pub 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 | |
| 358 | pub 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. | |
| 468 | pub 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. | |
| 484 | pub 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`. | |
| 495 | pub 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 | ||
| 506 | 274 | test { |
| 507 | 275 | _ = AtomicFile; |
| 508 | 276 | _ = Dir; |
lib/std/fs/test.zig+50-5| ... | ... | @@ -4,6 +4,7 @@ const native_os = builtin.os.tag; |
| 4 | 4 | const std = @import("../std.zig"); |
| 5 | 5 | const Io = std.Io; |
| 6 | 6 | const testing = std.testing; |
| 7 | const expect = std.testing.expect; | |
| 7 | 8 | const fs = std.fs; |
| 8 | 9 | const mem = std.mem; |
| 9 | 10 | const wasi = std.os.wasi; |
| ... | ... | @@ -1177,21 +1178,22 @@ test "renameAbsolute" { |
| 1177 | 1178 | dir.close(io); |
| 1178 | 1179 | } |
| 1179 | 1180 | |
| 1180 | test "openSelfExe" { | |
| 1181 | test "openExecutable" { | |
| 1181 | 1182 | if (native_os == .wasi) return error.SkipZigTest; |
| 1182 | 1183 | |
| 1183 | 1184 | const io = testing.io; |
| 1184 | 1185 | |
| 1185 | const self_exe_file = try std.fs.openSelfExe(.{}); | |
| 1186 | const self_exe_file = try std.fs.openExecutable(.{}); | |
| 1186 | 1187 | self_exe_file.close(io); |
| 1187 | 1188 | } |
| 1188 | 1189 | |
| 1189 | test "selfExePath" { | |
| 1190 | test "executablePath" { | |
| 1190 | 1191 | if (native_os == .wasi) return error.SkipZigTest; |
| 1191 | 1192 | |
| 1193 | const io = testing.io; | |
| 1192 | 1194 | var buf: [fs.max_path_bytes]u8 = undefined; |
| 1193 | const buf_self_exe_path = try std.fs.selfExePath(&buf); | |
| 1194 | const alloc_self_exe_path = try std.fs.selfExePathAlloc(testing.allocator); | |
| 1195 | const buf_self_exe_path = try std.process.executablePath(io, &buf); | |
| 1196 | const alloc_self_exe_path = try std.process.executablePathAlloc(io, testing.allocator); | |
| 1195 | 1197 | defer testing.allocator.free(alloc_self_exe_path); |
| 1196 | 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 | 2373 | try testing.expectEqualStrings("abcd", try check_r.interface.take(4)); |
| 2372 | 2374 | try testing.expectError(error.EndOfStream, check_r.interface.takeByte()); |
| 2373 | 2375 | } |
| 2376 | ||
| 2377 | test "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 | ||
| 2385 | fn 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 | ||
| 2391 | test "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 | 111 | } |
| 112 | 112 | } |
| 113 | 113 | |
| 114 | test "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 | ||
| 122 | fn 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 | ||
| 128 | 114 | fn getLinkInfo(fd: posix.fd_t) !struct { posix.ino_t, posix.nlink_t } { |
| 129 | 115 | if (native_os == .linux) { |
| 130 | 116 | const stx = try linux.wrapped.statx( |
| ... | ... | @@ -216,35 +202,6 @@ test "fstatat" { |
| 216 | 202 | // try expectEqual(stat.blocks, statat.blocks); |
| 217 | 203 | } |
| 218 | 204 | |
| 219 | test "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 | ||
| 248 | 205 | test "getrandom" { |
| 249 | 206 | var buf_a: [50]u8 = undefined; |
| 250 | 207 | var buf_b: [50]u8 = undefined; |
lib/std/process.zig+97-1| ... | ... | @@ -3,6 +3,7 @@ const native_os = builtin.os.tag; |
| 3 | 3 | |
| 4 | 4 | const std = @import("std.zig"); |
| 5 | 5 | const Io = std.Io; |
| 6 | const File = std.Io.File; | |
| 6 | 7 | const fs = std.fs; |
| 7 | 8 | const mem = std.mem; |
| 8 | 9 | const math = std.math; |
| ... | ... | @@ -12,6 +13,7 @@ const testing = std.testing; |
| 12 | 13 | const posix = std.posix; |
| 13 | 14 | const windows = std.os.windows; |
| 14 | 15 | const unicode = std.unicode; |
| 16 | const max_path_bytes = std.fs.max_path_bytes; | |
| 15 | 17 | |
| 16 | 18 | pub const Child = @import("process/Child.zig"); |
| 17 | 19 | pub const abort = posix.abort; |
| ... | ... | @@ -37,7 +39,7 @@ pub const GetCwdAllocError = Allocator.Error || error{CurrentWorkingDirectoryUnl |
| 37 | 39 | pub fn getCwdAlloc(allocator: Allocator) GetCwdAllocError![]u8 { |
| 38 | 40 | // The use of max_path_bytes here is just a heuristic: most paths will fit |
| 39 | 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 | 43 | var heap_buf: ?[]u8 = null; |
| 42 | 44 | defer if (heap_buf) |buf| allocator.free(buf); |
| 43 | 45 | |
| ... | ... | @@ -2112,3 +2114,97 @@ pub fn fatal(comptime format: []const u8, format_arguments: anytype) noreturn { |
| 2112 | 2114 | std.log.err(format, format_arguments); |
| 2113 | 2115 | exit(1); |
| 2114 | 2116 | } |
| 2117 | ||
| 2118 | pub 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 | ||
| 2154 | pub const ExecutablePathAllocError = ExecutablePathBaseError || Allocator.Error; | |
| 2155 | ||
| 2156 | pub 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 | ||
| 2165 | pub 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 | |
| 2183 | pub 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. | |
| 2193 | pub 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. | |
| 2201 | pub fn executableDirPathAlloc(allocator: Allocator) ![]u8 { | |
| 2202 | var buffer: [max_path_bytes]u8 = undefined; | |
| 2203 | return allocator.dupe(u8, try executableDirPath(&buffer)); | |
| 2204 | } | |
| 2205 | ||
| 2206 | pub const OpenExecutableError = File.OpenError || ExecutablePathError || File.LockError; | |
| 2207 | ||
| 2208 | pub 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 | 209 | DeviceBusy, |
| 210 | 210 | OSVersionDetectionFail, |
| 211 | 211 | Unexpected, |
| 212 | ProcessNotFound, | |
| 213 | 212 | } || Io.Cancelable; |
| 214 | 213 | |
| 215 | 214 | /// 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 | 421 | error.SocketUnconnected => return error.Unexpected, |
| 423 | 422 | |
| 424 | 423 | error.AccessDenied, |
| 425 | error.ProcessNotFound, | |
| 426 | 424 | error.SymLinkLoop, |
| 427 | 425 | error.ProcessFdQuotaExceeded, |
| 428 | 426 | error.SystemFdQuotaExceeded, |
| ... | ... | @@ -553,7 +551,6 @@ pub const AbiAndDynamicLinkerFromFileError = error{ |
| 553 | 551 | SystemResources, |
| 554 | 552 | ProcessFdQuotaExceeded, |
| 555 | 553 | SystemFdQuotaExceeded, |
| 556 | ProcessNotFound, | |
| 557 | 554 | IsDir, |
| 558 | 555 | WouldBlock, |
| 559 | 556 | InputOutput, |
| ... | ... | @@ -693,8 +690,10 @@ fn abiAndDynamicLinkerFromFile( |
| 693 | 690 | |
| 694 | 691 | // So far, no luck. Next we try to see if the information is |
| 695 | 692 | // present in the symlink data for the dynamic linker path. |
| 696 | var link_buf: [posix.PATH_MAX]u8 = undefined; | |
| 697 | const link_name = posix.readlink(dl_path, &link_buf) catch |err| switch (err) { | |
| 693 | var link_buffer: [posix.PATH_MAX]u8 = undefined; | |
| 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 | 697 | error.NameTooLong => unreachable, |
| 699 | 698 | error.BadPathName => unreachable, // Windows only |
| 700 | 699 | error.UnsupportedReparsePointType => unreachable, // Windows only |
| ... | ... | @@ -839,7 +838,6 @@ fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion { |
| 839 | 838 | error.NotDir => return error.GLibCNotFound, |
| 840 | 839 | error.IsDir => return error.GLibCNotFound, |
| 841 | 840 | |
| 842 | error.ProcessNotFound => |e| return e, | |
| 843 | 841 | error.ProcessFdQuotaExceeded => |e| return e, |
| 844 | 842 | error.SystemFdQuotaExceeded => |e| return e, |
| 845 | 843 | error.SystemResources => |e| return e, |
| ... | ... | @@ -1103,7 +1101,6 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ |
| 1103 | 1101 | error.SymLinkLoop, |
| 1104 | 1102 | error.ProcessFdQuotaExceeded, |
| 1105 | 1103 | error.SystemFdQuotaExceeded, |
| 1106 | error.ProcessNotFound, | |
| 1107 | 1104 | error.Canceled, |
| 1108 | 1105 | => |e| return e, |
| 1109 | 1106 |