authorgravatar for gereeter+code@gmail.comJonathan S <gereeter+code@gmail.com> 2020-03-27 14:28:48-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-29 18:23:06-04:00
log631633b25253201968b97cf129d986def37eed4a
treea09677ad9b9e71568c4ad384ac48a2a71710f628
parent4c8b937fb016a54b09d7447ca634b7cf1af78fce

Document and reduce usage of MAX_PATH_BYTES, lifting arbitrary buffer size requirements


4 files changed, 36 insertions(+), 11 deletions(-)

lib/std/debug.zig+3
...@@ -1283,6 +1283,9 @@ pub const DebugInfo = struct {...@@ -1283,6 +1283,9 @@ pub const DebugInfo = struct {
1283 const elf_path = if (ctx.name.len > 0)1283 const elf_path = if (ctx.name.len > 0)
1284 ctx.name1284 ctx.name
1285 else blk: {1285 else blk: {
1286 // Use of MAX_PATH_BYTES here is valid as the resulting path is immediately
1287 // opened with no modification. TODO: Use openSelfExe instead to avoid path
1288 // length limitations
1286 var buf: [fs.MAX_PATH_BYTES]u8 = undefined;1289 var buf: [fs.MAX_PATH_BYTES]u8 = undefined;
1287 break :blk try fs.selfExePath(&buf);1290 break :blk try fs.selfExePath(&buf);
1288 };1291 };
lib/std/fs.zig+28-10
...@@ -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 // 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 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
...@@ -1781,6 +1787,8 @@ pub fn openSelfExe() OpenSelfExeError!File {...@@ -1781,6 +1787,8 @@ pub fn openSelfExe() OpenSelfExeError!File {
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(), .{});
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;
...@@ -1792,6 +1800,8 @@ pub const SelfExePathError = os.ReadLinkError || os.SysCtlError;...@@ -1792,6 +1800,8 @@ 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 // TODO(#4812): Consider looping with larger and larger buffers to handle
1804 // overlong paths.
1795 var buf: [MAX_PATH_BYTES]u8 = undefined;1805 var buf: [MAX_PATH_BYTES]u8 = undefined;
1796 return mem.dupe(allocator, u8, try selfExePath(&buf));1806 return mem.dupe(allocator, u8, try selfExePath(&buf));
1797}1807}
...@@ -1806,10 +1816,10 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 {...@@ -1806,10 +1816,10 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 {
1806/// On Linux, depends on procfs being mounted. If the currently executing binary has1816/// 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)`.1817/// been deleted, the file path looks something like `/a/b/c/exe (deleted)`.
1808/// TODO make the return type of this a null terminated pointer1818/// TODO make the return type of this a null terminated pointer
1809pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 {1819pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
1810 if (is_darwin) {1820 if (is_darwin) {
1811 var u32_len: u32 = out_buffer.len;1821 var u32_len: u32 = @intCast(u32, math.min(out_buffer.len, math.maxInt(u32)));
1812 const rc = std.c._NSGetExecutablePath(out_buffer, &u32_len);1822 const rc = std.c._NSGetExecutablePath(out_buffer.ptr, &u32_len);
1813 if (rc != 0) return error.NameTooLong;1823 if (rc != 0) return error.NameTooLong;
1814 return mem.spanZ(@ptrCast([*:0]u8, out_buffer));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,14 +1828,14 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 {
1818 .freebsd, .dragonfly => {1828 .freebsd, .dragonfly => {
1819 var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC, os.KERN_PROC_PATHNAME, -1 };1829 var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC, os.KERN_PROC_PATHNAME, -1 };
1820 var out_len: usize = out_buffer.len;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 // TODO could this slice from 0 to out_len instead?1832 // TODO could this slice from 0 to out_len instead?
1823 return mem.spanZ(@ptrCast([*:0]u8, out_buffer));1833 return mem.spanZ(@ptrCast([*:0]u8, out_buffer));
1824 },1834 },
1825 .netbsd => {1835 .netbsd => {
1826 var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC_ARGS, -1, os.KERN_PROC_PATHNAME };1836 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;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 // TODO could this slice from 0 to out_len instead?1839 // TODO could this slice from 0 to out_len instead?
1830 return mem.spanZ(@ptrCast([*:0]u8, out_buffer));1840 return mem.spanZ(@ptrCast([*:0]u8, out_buffer));
1831 },1841 },
...@@ -1848,13 +1858,15 @@ pub fn selfExePathW() [:0]const u16 {...@@ -1848,13 +1858,15 @@ pub fn selfExePathW() [:0]const u16 {
1848/// `selfExeDirPath` except allocates the result on the heap.1858/// `selfExeDirPath` except allocates the result on the heap.
1849/// Caller owns returned memory.1859/// Caller owns returned memory.
1850pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 {1860pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 {
1861 // TODO(#4812): Consider looping with larger and larger buffers to handle
1862 // overlong paths.
1851 var buf: [MAX_PATH_BYTES]u8 = undefined;1863 var buf: [MAX_PATH_BYTES]u8 = undefined;
1852 return mem.dupe(allocator, u8, try selfExeDirPath(&buf));1864 return mem.dupe(allocator, u8, try selfExeDirPath(&buf));
1853}1865}
18541866
1855/// Get the directory path that contains the current executable.1867/// Get the directory path that contains the current executable.
1856/// Returned value is a slice of out_buffer.1868/// Returned value is a slice of out_buffer.
1857pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const u8 {1869pub fn selfExeDirPath(out_buffer: []u8) SelfExePathError![]const u8 {
1858 const self_exe_path = try selfExePath(out_buffer);1870 const self_exe_path = try selfExePath(out_buffer);
1859 // Assume that the OS APIs return absolute paths, and therefore dirname1871 // Assume that the OS APIs return absolute paths, and therefore dirname
1860 // will not return null.1872 // will not return null.
...@@ -1864,6 +1876,12 @@ pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const...@@ -1864,6 +1876,12 @@ pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const
1864/// `realpath`, except caller must free the returned memory.1876/// `realpath`, except caller must free the returned memory.
1865/// TODO integrate with `Dir`1877/// TODO integrate with `Dir`
1866pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {1878pub 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 var buf: [MAX_PATH_BYTES]u8 = undefined;1885 var buf: [MAX_PATH_BYTES]u8 = undefined;
1868 return mem.dupe(allocator, u8, try os.realpath(pathname, &buf));1886 return mem.dupe(allocator, u8, try os.realpath(pathname, &buf));
1869}1887}
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+3-1
...@@ -15,12 +15,14 @@ pub const changeCurDir = os.chdir;...@@ -15,12 +15,14 @@ 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 // TODO(#4812): Consider looping with larger and larger buffers to handle
25 // overlong paths.
24 var buf: [fs.MAX_PATH_BYTES]u8 = undefined;26 var buf: [fs.MAX_PATH_BYTES]u8 = undefined;
25 return mem.dupe(allocator, u8, try os.getcwd(&buf));27 return mem.dupe(allocator, u8, try os.getcwd(&buf));
26}28}