authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-29 18:41:40-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-29 18:41:40-04:00
logf107d654e073a7cc0c2e920ee6fac9b2b34dba0c
tree364c995c839ac3521522c2f9ca83368a162d8c66
parent4c8b937fb016a54b09d7447ca634b7cf1af78fce
parent89a97a7a278e69a440fd35665d4f1af864d023d9

Merge branch 'gereeter-reduced-path-max'

closes #4837

5 files changed, 98 insertions(+), 37 deletions(-)

lib/std/debug.zig+30-19
...@@ -670,10 +670,12 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {...@@ -670,10 +670,12 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {
670 }670 }
671}671}
672672
673/// This takes ownership of coff_file: users of this function should not close
674/// it themselves, even on error.
673/// TODO resources https://github.com/ziglang/zig/issues/4353675/// TODO resources https://github.com/ziglang/zig/issues/4353
674fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !ModuleDebugInfo {676/// TODO it's weird to take ownership even on error, rework this code.
677fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInfo {
675 nosuspend {678 nosuspend {
676 const coff_file = try std.fs.openFileAbsoluteW(coff_file_path, .{ .intended_io_mode = .blocking });
677 errdefer coff_file.close();679 errdefer coff_file.close();
678680
679 const coff_obj = try allocator.create(coff.Coff);681 const coff_obj = try allocator.create(coff.Coff);
...@@ -851,10 +853,13 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 {...@@ -851,10 +853,13 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 {
851 return ptr[start..end];853 return ptr[start..end];
852}854}
853855
856/// This takes ownership of elf_file: users of this function should not close
857/// it themselves, even on error.
854/// TODO resources https://github.com/ziglang/zig/issues/4353858/// TODO resources https://github.com/ziglang/zig/issues/4353
855pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ModuleDebugInfo {859/// TODO it's weird to take ownership even on error, rework this code.
860pub fn readElfDebugInfo(allocator: *mem.Allocator, elf_file: File) !ModuleDebugInfo {
856 nosuspend {861 nosuspend {
857 const mapped_mem = try mapWholeFile(elf_file_path);862 const mapped_mem = try mapWholeFile(elf_file);
858 const hdr = @ptrCast(*const elf.Ehdr, &mapped_mem[0]);863 const hdr = @ptrCast(*const elf.Ehdr, &mapped_mem[0]);
859 if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;864 if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
860 if (hdr.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;865 if (hdr.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
...@@ -921,8 +926,11 @@ pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !M...@@ -921,8 +926,11 @@ pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !M
921}926}
922927
923/// TODO resources https://github.com/ziglang/zig/issues/4353928/// TODO resources https://github.com/ziglang/zig/issues/4353
924fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !ModuleDebugInfo {929/// This takes ownership of coff_file: users of this function should not close
925 const mapped_mem = try mapWholeFile(macho_file_path);930/// it themselves, even on error.
931/// TODO it's weird to take ownership even on error, rework this code.
932fn readMachODebugInfo(allocator: *mem.Allocator, macho_file: File) !ModuleDebugInfo {
933 const mapped_mem = try mapWholeFile(macho_file);
926934
927 const hdr = @ptrCast(935 const hdr = @ptrCast(
928 *const macho.mach_header_64,936 *const macho.mach_header_64,
...@@ -1055,9 +1063,11 @@ const MachoSymbol = struct {...@@ -1055,9 +1063,11 @@ const MachoSymbol = struct {
1055 }1063 }
1056};1064};
10571065
1058fn mapWholeFile(path: []const u8) ![]align(mem.page_size) const u8 {1066/// `file` is expected to have been opened with .intended_io_mode == .blocking.
1067/// Takes ownership of file, even on error.
1068/// TODO it's weird to take ownership even on error, rework this code.
1069fn mapWholeFile(file: File) ![]align(mem.page_size) const u8 {
1059 nosuspend {1070 nosuspend {
1060 const file = try fs.cwd().openFile(path, .{ .intended_io_mode = .blocking });
1061 defer file.close();1071 defer file.close();
10621072
1063 const file_len = try math.cast(usize, try file.getEndPos());1073 const file_len = try math.cast(usize, try file.getEndPos());
...@@ -1140,10 +1150,11 @@ pub const DebugInfo = struct {...@@ -1140,10 +1150,11 @@ pub const DebugInfo = struct {
1140 errdefer self.allocator.destroy(obj_di);1150 errdefer self.allocator.destroy(obj_di);
11411151
1142 const macho_path = mem.spanZ(std.c._dyld_get_image_name(i));1152 const macho_path = mem.spanZ(std.c._dyld_get_image_name(i));
1143 obj_di.* = openMachODebugInfo(self.allocator, macho_path) catch |err| switch (err) {1153 const macho_file = fs.cwd().openFile(macho_path, .{ .intended_io_mode = .blocking }) catch |err| switch (err) {
1144 error.FileNotFound => return error.MissingDebugInfo,1154 error.FileNotFound => return error.MissingDebugInfo,
1145 else => return err,1155 else => return err,
1146 };1156 };
1157 obj_di.* = try readMachODebugInfo(self.allocator, macho_file);
1147 obj_di.base_address = base_address;1158 obj_di.base_address = base_address;
11481159
1149 try self.address_map.putNoClobber(base_address, obj_di);1160 try self.address_map.putNoClobber(base_address, obj_di);
...@@ -1221,10 +1232,11 @@ pub const DebugInfo = struct {...@@ -1221,10 +1232,11 @@ pub const DebugInfo = struct {
1221 const obj_di = try self.allocator.create(ModuleDebugInfo);1232 const obj_di = try self.allocator.create(ModuleDebugInfo);
1222 errdefer self.allocator.destroy(obj_di);1233 errdefer self.allocator.destroy(obj_di);
12231234
1224 obj_di.* = openCoffDebugInfo(self.allocator, name_buffer[0 .. len + 4 :0]) catch |err| switch (err) {1235 const coff_file = fs.openFileAbsoluteW(name_buffer[0 .. len + 4 :0], .{}) catch |err| switch (err) {
1225 error.FileNotFound => return error.MissingDebugInfo,1236 error.FileNotFound => return error.MissingDebugInfo,
1226 else => return err,1237 else => return err,
1227 };1238 };
1239 obj_di.* = try readCoffDebugInfo(self.allocator, coff_file);
1228 obj_di.base_address = seg_start;1240 obj_di.base_address = seg_start;
12291241
1230 try self.address_map.putNoClobber(seg_start, obj_di);1242 try self.address_map.putNoClobber(seg_start, obj_di);
...@@ -1280,20 +1292,18 @@ pub const DebugInfo = struct {...@@ -1280,20 +1292,18 @@ pub const DebugInfo = struct {
1280 return obj_di;1292 return obj_di;
1281 }1293 }
12821294
1283 const elf_path = if (ctx.name.len > 0)
1284 ctx.name
1285 else blk: {
1286 var buf: [fs.MAX_PATH_BYTES]u8 = undefined;
1287 break :blk try fs.selfExePath(&buf);
1288 };
1289
1290 const obj_di = try self.allocator.create(ModuleDebugInfo);1295 const obj_di = try self.allocator.create(ModuleDebugInfo);
1291 errdefer self.allocator.destroy(obj_di);1296 errdefer self.allocator.destroy(obj_di);
12921297
1293 obj_di.* = openElfDebugInfo(self.allocator, elf_path) catch |err| switch (err) {1298 const elf_file = (if (ctx.name.len > 0)
1299 fs.cwd().openFile(ctx.name, .{ .intended_io_mode = .blocking })
1300 else
1301 fs.openSelfExe(.{ .intended_io_mode = .blocking })) catch |err| switch (err) {
1294 error.FileNotFound => return error.MissingDebugInfo,1302 error.FileNotFound => return error.MissingDebugInfo,
1295 else => return err,1303 else => return err,
1296 };1304 };
1305
1306 obj_di.* = try readElfDebugInfo(self.allocator, elf_file);
1297 obj_di.base_address = ctx.base_address;1307 obj_di.base_address = ctx.base_address;
12981308
1299 try self.address_map.putNoClobber(ctx.base_address, obj_di);1309 try self.address_map.putNoClobber(ctx.base_address, obj_di);
...@@ -1329,7 +1339,8 @@ pub const ModuleDebugInfo = switch (builtin.os.tag) {...@@ -1329,7 +1339,8 @@ pub const ModuleDebugInfo = switch (builtin.os.tag) {
1329 }1339 }
13301340
1331 fn loadOFile(self: *@This(), o_file_path: []const u8) !DW.DwarfInfo {1341 fn loadOFile(self: *@This(), o_file_path: []const u8) !DW.DwarfInfo {
1332 const mapped_mem = try mapWholeFile(o_file_path);1342 const o_file = try fs.cwd().openFile(o_file_path, .{ .intended_io_mode = .blocking });
1343 const mapped_mem = try mapWholeFile(o_file);
13331344
1334 const hdr = @ptrCast(1345 const hdr = @ptrCast(
1335 *const macho.mach_header_64,1346 *const macho.mach_header_64,
lib/std/fs.zig+42-14
...@@ -33,8 +33,11 @@ pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirE...@@ -33,8 +33,11 @@ pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirE
3333
34pub const Watch = @import("fs/watch.zig").Watch;34pub const Watch = @import("fs/watch.zig").Watch;
3535
36/// This represents the maximum size of a UTF-8 encoded file path.36/// This represents the maximum size of a UTF-8 encoded file path that the
37/// All file system operations which return a path are guaranteed to37/// operating system will accept. Paths, including those returned from file
38/// system operations, may be longer than this length, but such paths cannot
39/// be successfully passed back in other file system operations. However,
40/// all path components returned by file system operations are assumed to
38/// fit into a UTF-8 encoded array of this length.41/// fit into a UTF-8 encoded array of this length.
39/// The byte count includes room for a null sentinel byte.42/// The byte count includes room for a null sentinel byte.
40pub const MAX_PATH_BYTES = switch (builtin.os.tag) {43pub const MAX_PATH_BYTES = switch (builtin.os.tag) {
...@@ -1194,7 +1197,7 @@ pub const Dir = struct {...@@ -1194,7 +1197,7 @@ pub const Dir = struct {
1194 /// Read value of a symbolic link.1197 /// Read value of a symbolic link.
1195 /// The return value is a slice of `buffer`, from index `0`.1198 /// The return value is a slice of `buffer`, from index `0`.
1196 /// Asserts that the path parameter has no null bytes.1199 /// Asserts that the path parameter has no null bytes.
1197 pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {1200 pub fn readLink(self: Dir, sub_path: []const u8, buffer: []u8) ![]u8 {
1198 const sub_path_c = try os.toPosixPath(sub_path);1201 const sub_path_c = try os.toPosixPath(sub_path);
1199 return self.readLinkZ(&sub_path_c, buffer);1202 return self.readLinkZ(&sub_path_c, buffer);
1200 }1203 }
...@@ -1202,7 +1205,7 @@ pub const Dir = struct {...@@ -1202,7 +1205,7 @@ pub const Dir = struct {
1202 pub const readLinkC = @compileError("deprecated: renamed to readLinkZ");1205 pub const readLinkC = @compileError("deprecated: renamed to readLinkZ");
12031206
1204 /// Same as `readLink`, except the `pathname` parameter is null-terminated.1207 /// Same as `readLink`, except the `pathname` parameter is null-terminated.
1205 pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {1208 pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {
1206 return os.readlinkatZ(self.fd, sub_path_c, buffer);1209 return os.readlinkatZ(self.fd, sub_path_c, buffer);
1207 }1210 }
12081211
...@@ -1320,6 +1323,9 @@ pub const Dir = struct {...@@ -1320,6 +1323,9 @@ pub const Dir = struct {
1320 var cleanup_dir = true;1323 var cleanup_dir = true;
1321 defer if (cleanup_dir) dir.close();1324 defer if (cleanup_dir) dir.close();
13221325
1326 // Valid use of MAX_PATH_BYTES because dir_name_buf will only
1327 // ever store a single path component that was returned from the
1328 // filesystem.
1323 var dir_name_buf: [MAX_PATH_BYTES]u8 = undefined;1329 var dir_name_buf: [MAX_PATH_BYTES]u8 = undefined;
1324 var dir_name: []const u8 = sub_path;1330 var dir_name: []const u8 = sub_path;
13251331
...@@ -1772,19 +1778,21 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {...@@ -1772,19 +1778,21 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
17721778
1773pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError || os.FlockError;1779pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError || os.FlockError;
17741780
1775pub fn openSelfExe() OpenSelfExeError!File {1781pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {
1776 if (builtin.os.tag == .linux) {1782 if (builtin.os.tag == .linux) {
1777 return openFileAbsoluteZ("/proc/self/exe", .{});1783 return openFileAbsoluteZ("/proc/self/exe", flags);
1778 }1784 }
1779 if (builtin.os.tag == .windows) {1785 if (builtin.os.tag == .windows) {
1780 const wide_slice = selfExePathW();1786 const wide_slice = selfExePathW();
1781 const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice);1787 const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice);
1782 return cwd().openFileW(prefixed_path_w.span(), .{});1788 return cwd().openFileW(prefixed_path_w.span(), flags);
1783 }1789 }
1790 // Use of MAX_PATH_BYTES here is valid as the resulting path is immediately
1791 // opened with no modification.
1784 var buf: [MAX_PATH_BYTES]u8 = undefined;1792 var buf: [MAX_PATH_BYTES]u8 = undefined;
1785 const self_exe_path = try selfExePath(&buf);1793 const self_exe_path = try selfExePath(&buf);
1786 buf[self_exe_path.len] = 0;1794 buf[self_exe_path.len] = 0;
1787 return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, .{});1795 return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, flags);
1788}1796}
17891797
1790pub const SelfExePathError = os.ReadLinkError || os.SysCtlError;1798pub const SelfExePathError = os.ReadLinkError || os.SysCtlError;
...@@ -1792,6 +1800,13 @@ pub const SelfExePathError = os.ReadLinkError || os.SysCtlError;...@@ -1792,6 +1800,13 @@ pub const SelfExePathError = os.ReadLinkError || os.SysCtlError;
1792/// `selfExePath` except allocates the result on the heap.1800/// `selfExePath` except allocates the result on the heap.
1793/// Caller owns returned memory.1801/// Caller owns returned memory.
1794pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 {1802pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 {
1803 // Use of MAX_PATH_BYTES here is justified as, at least on one tested Linux
1804 // system, readlink will completely fail to return a result larger than
1805 // PATH_MAX even if given a sufficiently large buffer. This makes it
1806 // fundamentally impossible to get the selfExePath of a program running in
1807 // a very deeply nested directory chain in this way.
1808 // TODO(#4812): Investigate other systems and whether it is possible to get
1809 // this path by trying larger and larger buffers until one succeeds.
1795 var buf: [MAX_PATH_BYTES]u8 = undefined;1810 var buf: [MAX_PATH_BYTES]u8 = undefined;
1796 return mem.dupe(allocator, u8, try selfExePath(&buf));1811 return mem.dupe(allocator, u8, try selfExePath(&buf));
1797}1812}
...@@ -1806,10 +1821,10 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 {...@@ -1806,10 +1821,10 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 {
1806/// On Linux, depends on procfs being mounted. If the currently executing binary has1821/// On Linux, depends on procfs being mounted. If the currently executing binary has
1807/// been deleted, the file path looks something like `/a/b/c/exe (deleted)`.1822/// been deleted, the file path looks something like `/a/b/c/exe (deleted)`.
1808/// TODO make the return type of this a null terminated pointer1823/// TODO make the return type of this a null terminated pointer
1809pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 {1824pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
1810 if (is_darwin) {1825 if (is_darwin) {
1811 var u32_len: u32 = out_buffer.len;1826 var u32_len: u32 = @intCast(u32, math.min(out_buffer.len, math.maxInt(u32)));
1812 const rc = std.c._NSGetExecutablePath(out_buffer, &u32_len);1827 const rc = std.c._NSGetExecutablePath(out_buffer.ptr, &u32_len);
1813 if (rc != 0) return error.NameTooLong;1828 if (rc != 0) return error.NameTooLong;
1814 return mem.spanZ(@ptrCast([*:0]u8, out_buffer));1829 return mem.spanZ(@ptrCast([*:0]u8, out_buffer));
1815 }1830 }
...@@ -1818,14 +1833,14 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 {...@@ -1818,14 +1833,14 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 {
1818 .freebsd, .dragonfly => {1833 .freebsd, .dragonfly => {
1819 var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC, os.KERN_PROC_PATHNAME, -1 };1834 var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC, os.KERN_PROC_PATHNAME, -1 };
1820 var out_len: usize = out_buffer.len;1835 var out_len: usize = out_buffer.len;
1821 try os.sysctl(&mib, out_buffer, &out_len, null, 0);1836 try os.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
1822 // TODO could this slice from 0 to out_len instead?1837 // TODO could this slice from 0 to out_len instead?
1823 return mem.spanZ(@ptrCast([*:0]u8, out_buffer));1838 return mem.spanZ(@ptrCast([*:0]u8, out_buffer));
1824 },1839 },
1825 .netbsd => {1840 .netbsd => {
1826 var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC_ARGS, -1, os.KERN_PROC_PATHNAME };1841 var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC_ARGS, -1, os.KERN_PROC_PATHNAME };
1827 var out_len: usize = out_buffer.len;1842 var out_len: usize = out_buffer.len;
1828 try os.sysctl(&mib, out_buffer, &out_len, null, 0);1843 try os.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
1829 // TODO could this slice from 0 to out_len instead?1844 // TODO could this slice from 0 to out_len instead?
1830 return mem.spanZ(@ptrCast([*:0]u8, out_buffer));1845 return mem.spanZ(@ptrCast([*:0]u8, out_buffer));
1831 },1846 },
...@@ -1848,13 +1863,20 @@ pub fn selfExePathW() [:0]const u16 {...@@ -1848,13 +1863,20 @@ pub fn selfExePathW() [:0]const u16 {
1848/// `selfExeDirPath` except allocates the result on the heap.1863/// `selfExeDirPath` except allocates the result on the heap.
1849/// Caller owns returned memory.1864/// Caller owns returned memory.
1850pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 {1865pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 {
1866 // Use of MAX_PATH_BYTES here is justified as, at least on one tested Linux
1867 // system, readlink will completely fail to return a result larger than
1868 // PATH_MAX even if given a sufficiently large buffer. This makes it
1869 // fundamentally impossible to get the selfExeDirPath of a program running
1870 // in a very deeply nested directory chain in this way.
1871 // TODO(#4812): Investigate other systems and whether it is possible to get
1872 // this path by trying larger and larger buffers until one succeeds.
1851 var buf: [MAX_PATH_BYTES]u8 = undefined;1873 var buf: [MAX_PATH_BYTES]u8 = undefined;
1852 return mem.dupe(allocator, u8, try selfExeDirPath(&buf));1874 return mem.dupe(allocator, u8, try selfExeDirPath(&buf));
1853}1875}
18541876
1855/// Get the directory path that contains the current executable.1877/// Get the directory path that contains the current executable.
1856/// Returned value is a slice of out_buffer.1878/// Returned value is a slice of out_buffer.
1857pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const u8 {1879pub fn selfExeDirPath(out_buffer: []u8) SelfExePathError![]const u8 {
1858 const self_exe_path = try selfExePath(out_buffer);1880 const self_exe_path = try selfExePath(out_buffer);
1859 // Assume that the OS APIs return absolute paths, and therefore dirname1881 // Assume that the OS APIs return absolute paths, and therefore dirname
1860 // will not return null.1882 // will not return null.
...@@ -1864,6 +1886,12 @@ pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const...@@ -1864,6 +1886,12 @@ pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const
1864/// `realpath`, except caller must free the returned memory.1886/// `realpath`, except caller must free the returned memory.
1865/// TODO integrate with `Dir`1887/// TODO integrate with `Dir`
1866pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {1888pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
1889 // Use of MAX_PATH_BYTES here is valid as the realpath function does not
1890 // have a variant that takes an arbitrary-size buffer.
1891 // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008
1892 // NULL out parameter (GNU's canonicalize_file_name) to handle overelong
1893 // paths. musl supports passing NULL but restricts the output to PATH_MAX
1894 // anyway.
1867 var buf: [MAX_PATH_BYTES]u8 = undefined;1895 var buf: [MAX_PATH_BYTES]u8 = undefined;
1868 return mem.dupe(allocator, u8, try os.realpath(pathname, &buf));1896 return mem.dupe(allocator, u8, try os.realpath(pathname, &buf));
1869}1897}
lib/std/fs/test.zig+1-1
...@@ -6,7 +6,7 @@ const File = std.fs.File;...@@ -6,7 +6,7 @@ const File = std.fs.File;
6test "openSelfExe" {6test "openSelfExe" {
7 if (builtin.os.tag == .wasi) return error.SkipZigTest;7 if (builtin.os.tag == .wasi) return error.SkipZigTest;
88
9 const self_exe_file = try std.fs.openSelfExe();9 const self_exe_file = try std.fs.openSelfExe(.{});
10 self_exe_file.close();10 self_exe_file.close();
11}11}
1212
lib/std/os.zig+2
...@@ -1236,6 +1236,8 @@ pub fn execvpeZ_expandArg0(...@@ -1236,6 +1236,8 @@ pub fn execvpeZ_expandArg0(
1236 if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveZ(file, child_argv, envp);1236 if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveZ(file, child_argv, envp);
12371237
1238 const PATH = getenvZ("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";1238 const PATH = getenvZ("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";
1239 // Use of MAX_PATH_BYTES here is valid as the path_buf will be passed
1240 // directly to the operating system in execveZ.
1239 var path_buf: [MAX_PATH_BYTES]u8 = undefined;1241 var path_buf: [MAX_PATH_BYTES]u8 = undefined;
1240 var it = mem.tokenize(PATH, ":");1242 var it = mem.tokenize(PATH, ":");
1241 var seen_eacces = false;1243 var seen_eacces = false;
lib/std/process.zig+23-3
...@@ -15,14 +15,34 @@ pub const changeCurDir = os.chdir;...@@ -15,14 +15,34 @@ pub const changeCurDir = os.chdir;
15pub const changeCurDirC = os.chdirC;15pub const changeCurDirC = os.chdirC;
1616
17/// The result is a slice of `out_buffer`, from index `0`.17/// The result is a slice of `out_buffer`, from index `0`.
18pub fn getCwd(out_buffer: *[fs.MAX_PATH_BYTES]u8) ![]u8 {18pub fn getCwd(out_buffer: []u8) ![]u8 {
19 return os.getcwd(out_buffer);19 return os.getcwd(out_buffer);
20}20}
2121
22/// Caller must free the returned memory.22/// Caller must free the returned memory.
23pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {23pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {
24 var buf: [fs.MAX_PATH_BYTES]u8 = undefined;24 // The use of MAX_PATH_BYTES here is just a heuristic: most paths will fit
25 return mem.dupe(allocator, u8, try os.getcwd(&buf));25 // in stack_buf, avoiding an extra allocation in the common case.
26 var stack_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
27 var heap_buf: ?[]u8 = null;
28 defer if (heap_buf) |buf| allocator.free(buf);
29
30 var current_buf: []u8 = &stack_buf;
31 while (true) {
32 if (os.getcwd(current_buf)) |slice| {
33 return mem.dupe(allocator, u8, slice);
34 } else |err| switch (err) {
35 error.NameTooLong => {
36 // The path is too long to fit in stack_buf. Allocate geometrically
37 // increasing buffers until we find one that works
38 const new_capacity = current_buf.len * 2;
39 if (heap_buf) |buf| allocator.free(buf);
40 current_buf = try allocator.alloc(u8, new_capacity);
41 heap_buf = current_buf;
42 },
43 else => |e| return e,
44 }
45 }
26}46}
2747
28test "getCwdAlloc" {48test "getCwdAlloc" {