From a0226ab05b50341622ace8314bdad3da7cd7e35d Mon Sep 17 00:00:00 2001 From: g-w1 <58830309+g-w1@users.noreply.github.com> Date: Wed, 18 Nov 2020 02:42:35 -0500 Subject: [PATCH] std: openDirAbsolute and accessAbsolute (#7082) * add more abosolutes * added wrong files * adding 2 tests and changing the function signatures because of lazy analysis not checking them * fix a bug that got uncovered by lazy eval * Add compile error when using WASI with openDirAbsolute and accessAbsolute * typo --- lib/std/fs.zig | 64 +++++++++++++++++++++++++++++++++++++++++++-- lib/std/fs/test.zig | 34 ++++++++++++++++++++++++ lib/std/os.zig | 6 ++--- 3 files changed, 99 insertions(+), 5 deletions(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 4258b7a72..0b69c6e53 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -1883,11 +1883,41 @@ pub fn cwd() Dir { } } +/// Opens a directory at the given path. The directory is a system resource that remains +/// open until `close` is called on the result. +/// See `openDirAbsoluteZ` for a function that accepts a null-terminated path. +/// +/// Asserts that the path parameter has no null bytes. +pub fn openDirAbsolute(absolute_path: []const u8, flags: Dir.OpenDirOptions) File.OpenError!Dir { + if (builtin.os.tag == .wasi) { + @compileError("WASI doesn't have the concept of an absolute directory; use openDir instead for WASI."); + } + assert(path.isAbsolute(absolute_path)); + return cwd().openDir(absolute_path, flags); +} + +/// Same as `openDirAbsolute` but the path parameter is null-terminated. +pub fn openDirAbsoluteZ(absolute_path_c: [*:0]const u8, flags: Dir.OpenDirOptions) File.OpenError!Dir { + if (builtin.os.tag == .wasi) { + @compileError("WASI doesn't have the concept of an absolute directory; use openDir instead for WASI."); + } + assert(path.isAbsoluteZ(absolute_path_c)); + return cwd().openDirZ(absolute_path_c, flags); +} +/// Same as `openDirAbsolute` but the path parameter is null-terminated. +pub fn openDirAbsoluteW(absolute_path_c: [*:0]const u16, flags: Dir.OpenDirOptions) File.OpenError!Dir { + if (builtin.os.tag == .wasi) { + @compileError("WASI doesn't have the concept of an absolute directory; use openDir instead for WASI."); + } + assert(path.isAbsoluteWindowsW(absolute_path_c)); + return cwd().openDirW(absolute_path_c, flags); +} + /// Opens a file for reading or writing, without attempting to create a new file, based on an absolute path. /// Call `File.close` to release the resource. /// Asserts that the path is absolute. See `Dir.openFile` for a function that /// operates on both absolute and relative paths. -/// Asserts that the path parameter has no null bytes. See `openFileAbsoluteC` for a function +/// Asserts that the path parameter has no null bytes. See `openFileAbsoluteZ` for a function /// that accepts a null-terminated path. pub fn openFileAbsolute(absolute_path: []const u8, flags: File.OpenFlags) File.OpenError!File { assert(path.isAbsolute(absolute_path)); @@ -1908,6 +1938,36 @@ pub fn openFileAbsoluteW(absolute_path_w: []const u16, flags: File.OpenFlags) Fi return cwd().openFileW(absolute_path_w, flags); } +/// Test accessing `path`. +/// `path` is UTF8-encoded. +/// Be careful of Time-Of-Check-Time-Of-Use race conditions when using this function. +/// For example, instead of testing if a file exists and then opening it, just +/// open it and handle the error for file not found. +/// See `accessAbsoluteZ` for a function that accepts a null-terminated path. +pub fn accessAbsolute(absolute_path: []const u8, flags: File.OpenFlags) Dir.AccessError!void { + if (builtin.os.tag == .wasi) { + @compileError("WASI doesn't have the concept of an absolute path; use access instead for WASI."); + } + assert(path.isAbsolute(absolute_path)); + try cwd().access(absolute_path, flags); +} +/// Same as `accessAbsolute` but the path parameter is null-terminated. +pub fn accessAbsoluteZ(absolute_path: [*:0]const u8, flags: File.OpenFlags) Dir.AccessError!void { + if (builtin.os.tag == .wasi) { + @compileError("WASI doesn't have the concept of an absolute path; use access instead for WASI."); + } + assert(path.isAbsoluteZ(absolute_path)); + try cwd().accessZ(absolute_path, flags); +} +/// Same as `accessAbsolute` but the path parameter is WTF-16 encoded. +pub fn accessAbsoluteW(absolute_path: [*:0]const 16, flags: File.OpenFlags) Dir.AccessError!void { + if (builtin.os.tag == .wasi) { + @compileError("WASI doesn't have the concept of an absolute path; use access instead for WASI."); + } + assert(path.isAbsoluteWindowsW(absolute_path)); + try cwd().accessW(absolute_path, flags); +} + /// Creates, opens, or overwrites a file with write access, based on an absolute path. /// Call `File.close` to release the resource. /// Asserts that the path is absolute. See `Dir.createFile` for a function that @@ -2252,7 +2312,7 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { const PATH = std.os.getenvZ("PATH") orelse return error.FileNotFound; var path_it = mem.tokenize(PATH, &[_]u8{path.delimiter}); while (path_it.next()) |a_path| { - var resolved_path_buf: [MAX_PATH_BYTES-1:0]u8 = undefined; + var resolved_path_buf: [MAX_PATH_BYTES - 1:0]u8 = undefined; const resolved_path = std.fmt.bufPrintZ(&resolved_path_buf, "{s}/{s}", .{ a_path, os.argv[0], diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 8c24265db..5469192f6 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -49,6 +49,40 @@ fn testReadLink(dir: Dir, target_path: []const u8, symlink_path: []const u8) !vo testing.expect(mem.eql(u8, target_path, given)); } +test "accessAbsolute" { + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + var tmp = tmpDir(.{}); + defer tmp.cleanup(); + + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const base_path = blk: { + const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] }); + break :blk try fs.realpathAlloc(&arena.allocator, relative_path); + }; + + try fs.accessAbsolute(base_path, .{}); +} + +test "openDirAbsolute" { + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + var tmp = tmpDir(.{}); + defer tmp.cleanup(); + + try tmp.dir.makeDir("subdir"); + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const base_path = blk: { + const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..], "subdir" }); + break :blk try fs.realpathAlloc(&arena.allocator, relative_path); + }; + + var dir = try fs.openDirAbsolute(base_path, .{}); + defer dir.close(); +} + test "readLinkAbsolute" { if (builtin.os.tag == .wasi) return error.SkipZigTest; diff --git a/lib/std/os.zig b/lib/std/os.zig index 2ea26f8b1..91b88304f 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -4453,11 +4453,11 @@ pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t { /// Used to convert a slice to a null terminated slice on the stack. /// TODO https://github.com/ziglang/zig/issues/287 -pub fn toPosixPath(file_path: []const u8) ![PATH_MAX - 1:0]u8 { +pub fn toPosixPath(file_path: []const u8) ![MAX_PATH_BYTES - 1:0]u8 { if (std.debug.runtime_safety) assert(std.mem.indexOfScalar(u8, file_path, 0) == null); - var path_with_null: [PATH_MAX - 1:0]u8 = undefined; + var path_with_null: [MAX_PATH_BYTES - 1:0]u8 = undefined; // >= rather than > to make room for the null byte - if (file_path.len >= PATH_MAX) return error.NameTooLong; + if (file_path.len >= MAX_PATH_BYTES) return error.NameTooLong; mem.copy(u8, &path_with_null, file_path); path_with_null[file_path.len] = 0; return path_with_null;