| ... | @@ -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 |
| 33 | | 33 | |
| 34 | pub const Watch = @import("fs/watch.zig").Watch; | 34 | pub const Watch = @import("fs/watch.zig").Watch; |
| 35 | | 35 | |
| 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 to | 37 | /// 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. |
| 40 | pub const MAX_PATH_BYTES = switch (builtin.os.tag) { | 43 | pub 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"); |
| 1203 | | 1206 | |
| 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 | } |
| 1208 | | 1211 | |
| ... | @@ -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(); |
| 1322 | | 1325 | |
| | 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; |
| 1325 | | 1331 | |
| ... | @@ -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 { |
| 1772 | | 1778 | |
| 1773 | pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError || os.FlockError; | 1779 | pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError || os.FlockError; |
| 1774 | | 1780 | |
| 1775 | pub fn openSelfExe() OpenSelfExeError!File { | 1781 | pub 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 | } |
| 1789 | | 1797 | |
| 1790 | pub const SelfExePathError = os.ReadLinkError || os.SysCtlError; | 1798 | pub 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. |
| 1794 | pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 { | 1802 | pub 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 has | 1821 | /// 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 pointer | 1823 | /// TODO make the return type of this a null terminated pointer |
| 1809 | pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 { | 1824 | pub 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. |
| 1850 | pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 { | 1865 | pub 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 | } |
| 1854 | | 1876 | |
| 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. |
| 1857 | pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const u8 { | 1879 | pub 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 dirname | 1881 | // 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` |
| 1866 | pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 { | 1888 | pub 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 | } |