| ... | ... | @@ -33,8 +33,11 @@ pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirE |
| 33 | 33 | |
| 34 | 34 | pub const Watch = @import("fs/watch.zig").Watch; |
| 35 | 35 | |
| 36 | | /// This represents the maximum size of a UTF-8 encoded file path. |
| 37 | | /// All file system operations which return a path are guaranteed to |
| 36 | /// This represents the maximum size of a UTF-8 encoded file path that the |
| 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 | 41 | /// fit into a UTF-8 encoded array of this length. |
| 39 | 42 | /// The byte count includes room for a null sentinel byte. |
| 40 | 43 | pub const MAX_PATH_BYTES = switch (builtin.os.tag) { |
| ... | ... | @@ -1194,7 +1197,7 @@ pub const Dir = struct { |
| 1194 | 1197 | /// Read value of a symbolic link. |
| 1195 | 1198 | /// The return value is a slice of `buffer`, from index `0`. |
| 1196 | 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 | 1201 | const sub_path_c = try os.toPosixPath(sub_path); |
| 1199 | 1202 | return self.readLinkZ(&sub_path_c, buffer); |
| 1200 | 1203 | } |
| ... | ... | @@ -1202,7 +1205,7 @@ pub const Dir = struct { |
| 1202 | 1205 | pub const readLinkC = @compileError("deprecated: renamed to readLinkZ"); |
| 1203 | 1206 | |
| 1204 | 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 | 1209 | return os.readlinkatZ(self.fd, sub_path_c, buffer); |
| 1207 | 1210 | } |
| 1208 | 1211 | |
| ... | ... | @@ -1320,6 +1323,9 @@ pub const Dir = struct { |
| 1320 | 1323 | var cleanup_dir = true; |
| 1321 | 1324 | defer if (cleanup_dir) dir.close(); |
| 1322 | 1325 | |
| 1326 | // Likely valid use of MAX_PATH_BYTES, as dir_name_buf will only |
| 1327 | // ever store a single path component that was returned from the |
| 1328 | // filesystem. |
| 1323 | 1329 | var dir_name_buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1324 | 1330 | var dir_name: []const u8 = sub_path; |
| 1325 | 1331 | |
| ... | ... | @@ -1781,6 +1787,8 @@ pub fn openSelfExe() OpenSelfExeError!File { |
| 1781 | 1787 | const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice); |
| 1782 | 1788 | return cwd().openFileW(prefixed_path_w.span(), .{}); |
| 1783 | 1789 | } |
| 1790 | // Use of MAX_PATH_BYTES here is valid as the resulting path is immediately |
| 1791 | // opened with no modification. |
| 1784 | 1792 | var buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1785 | 1793 | const self_exe_path = try selfExePath(&buf); |
| 1786 | 1794 | buf[self_exe_path.len] = 0; |
| ... | ... | @@ -1792,6 +1800,8 @@ pub const SelfExePathError = os.ReadLinkError || os.SysCtlError; |
| 1792 | 1800 | /// `selfExePath` except allocates the result on the heap. |
| 1793 | 1801 | /// Caller owns returned memory. |
| 1794 | 1802 | pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 { |
| 1803 | // TODO(#4812): Consider looping with larger and larger buffers to handle |
| 1804 | // overlong paths. |
| 1795 | 1805 | var buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1796 | 1806 | return mem.dupe(allocator, u8, try selfExePath(&buf)); |
| 1797 | 1807 | } |
| ... | ... | @@ -1806,10 +1816,10 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 { |
| 1806 | 1816 | /// On Linux, depends on procfs being mounted. If the currently executing binary has |
| 1807 | 1817 | /// been deleted, the file path looks something like `/a/b/c/exe (deleted)`. |
| 1808 | 1818 | /// TODO make the return type of this a null terminated pointer |
| 1809 | | pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 { |
| 1819 | pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { |
| 1810 | 1820 | if (is_darwin) { |
| 1811 | | var u32_len: u32 = out_buffer.len; |
| 1812 | | const rc = std.c._NSGetExecutablePath(out_buffer, &u32_len); |
| 1821 | var u32_len: u32 = @intCast(u32, math.min(out_buffer.len, math.maxInt(u32))); |
| 1822 | const rc = std.c._NSGetExecutablePath(out_buffer.ptr, &u32_len); |
| 1813 | 1823 | if (rc != 0) return error.NameTooLong; |
| 1814 | 1824 | return mem.spanZ(@ptrCast([*:0]u8, out_buffer)); |
| 1815 | 1825 | } |
| ... | ... | @@ -1818,14 +1828,14 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 { |
| 1818 | 1828 | .freebsd, .dragonfly => { |
| 1819 | 1829 | var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC, os.KERN_PROC_PATHNAME, -1 }; |
| 1820 | 1830 | var out_len: usize = out_buffer.len; |
| 1821 | | try os.sysctl(&mib, out_buffer, &out_len, null, 0); |
| 1831 | try os.sysctl(&mib, out_buffer.ptr, &out_len, null, 0); |
| 1822 | 1832 | // TODO could this slice from 0 to out_len instead? |
| 1823 | 1833 | return mem.spanZ(@ptrCast([*:0]u8, out_buffer)); |
| 1824 | 1834 | }, |
| 1825 | 1835 | .netbsd => { |
| 1826 | 1836 | var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC_ARGS, -1, os.KERN_PROC_PATHNAME }; |
| 1827 | 1837 | var out_len: usize = out_buffer.len; |
| 1828 | | try os.sysctl(&mib, out_buffer, &out_len, null, 0); |
| 1838 | try os.sysctl(&mib, out_buffer.ptr, &out_len, null, 0); |
| 1829 | 1839 | // TODO could this slice from 0 to out_len instead? |
| 1830 | 1840 | return mem.spanZ(@ptrCast([*:0]u8, out_buffer)); |
| 1831 | 1841 | }, |
| ... | ... | @@ -1848,13 +1858,15 @@ pub fn selfExePathW() [:0]const u16 { |
| 1848 | 1858 | /// `selfExeDirPath` except allocates the result on the heap. |
| 1849 | 1859 | /// Caller owns returned memory. |
| 1850 | 1860 | pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 { |
| 1861 | // TODO(#4812): Consider looping with larger and larger buffers to handle |
| 1862 | // overlong paths. |
| 1851 | 1863 | var buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1852 | 1864 | return mem.dupe(allocator, u8, try selfExeDirPath(&buf)); |
| 1853 | 1865 | } |
| 1854 | 1866 | |
| 1855 | 1867 | /// Get the directory path that contains the current executable. |
| 1856 | 1868 | /// Returned value is a slice of out_buffer. |
| 1857 | | pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const u8 { |
| 1869 | pub fn selfExeDirPath(out_buffer: []u8) SelfExePathError![]const u8 { |
| 1858 | 1870 | const self_exe_path = try selfExePath(out_buffer); |
| 1859 | 1871 | // Assume that the OS APIs return absolute paths, and therefore dirname |
| 1860 | 1872 | // will not return null. |
| ... | ... | @@ -1864,6 +1876,12 @@ pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const |
| 1864 | 1876 | /// `realpath`, except caller must free the returned memory. |
| 1865 | 1877 | /// TODO integrate with `Dir` |
| 1866 | 1878 | pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 { |
| 1879 | // Use of MAX_PATH_BYTES here is valid as the realpath function does not |
| 1880 | // have a variant that takes an arbitrary-size buffer. |
| 1881 | // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008 |
| 1882 | // NULL out parameter (GNU's canonicalize_file_name) to handle overelong |
| 1883 | // paths. musl supports passing NULL but restricts the output to PATH_MAX |
| 1884 | // anyway. |
| 1867 | 1885 | var buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1868 | 1886 | return mem.dupe(allocator, u8, try os.realpath(pathname, &buf)); |
| 1869 | 1887 | } |