diff options
Diffstat (limited to 'lib/std')
41 files changed, 621 insertions, 394 deletions
diff --git a/lib/std/Build/Cache/Directory.zig b/lib/std/Build/Cache/Directory.zig index 305ef25361..ce5f5b02bb 100644 --- a/lib/std/Build/Cache/Directory.zig +++ b/lib/std/Build/Cache/Directory.zig @@ -52,8 +52,8 @@ pub fn joinZ(self: Directory, allocator: Allocator, paths: []const []const u8) ! /// Whether or not the handle should be closed, or the path should be freed /// is determined by usage, however this function is provided for convenience /// if it happens to be what the caller needs. -pub fn closeAndFree(self: *Directory, gpa: Allocator) void { - self.handle.close(); +pub fn closeAndFree(self: *Directory, gpa: Allocator, io: Io) void { + self.handle.close(io); if (self.path) |p| gpa.free(p); self.* = undefined; } diff --git a/lib/std/Build/Fuzz.zig b/lib/std/Build/Fuzz.zig index 2897b29969..db83f393fd 100644 --- a/lib/std/Build/Fuzz.zig +++ b/lib/std/Build/Fuzz.zig @@ -411,7 +411,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO }); return error.AlreadyReported; }; - defer coverage_file.close(); + defer coverage_file.close(io); const file_size = coverage_file.getEndPos() catch |err| { log.err("unable to check len of coverage file '{f}': {t}", .{ coverage_file_path, err }); @@ -533,7 +533,7 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void { cov.run.step.name, coverage_file_path, err, }); }; - defer coverage_file.close(); + defer coverage_file.close(io); const fuzz_abi = std.Build.abi.fuzz; var rbuf: [0x1000]u8 = undefined; diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 33fe755c2b..acde47071d 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -441,6 +441,7 @@ pub fn evalZigProcess( assert(argv.len != 0); const b = s.owner; const arena = b.allocator; + const io = b.graph.io; try handleChildProcUnsupported(s); try handleVerbose(s.owner, null, argv); @@ -474,7 +475,7 @@ pub fn evalZigProcess( if (!watch) { // Send EOF to stdin. - zp.child.stdin.?.close(); + zp.child.stdin.?.close(io); zp.child.stdin = null; const term = zp.child.wait() catch |err| { diff --git a/lib/std/Build/Step/InstallArtifact.zig b/lib/std/Build/Step/InstallArtifact.zig index c203ae924b..1cdb232770 100644 --- a/lib/std/Build/Step/InstallArtifact.zig +++ b/lib/std/Build/Step/InstallArtifact.zig @@ -119,6 +119,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { _ = options; const install_artifact: *InstallArtifact = @fieldParentPtr("step", step); const b = step.owner; + const io = b.graph.io; var all_cached = true; @@ -168,7 +169,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { src_dir_path, @errorName(err), }); }; - defer src_dir.close(); + defer src_dir.close(io); var it = try src_dir.walk(b.allocator); next_entry: while (try it.next()) |entry| { diff --git a/lib/std/Build/Step/InstallDir.zig b/lib/std/Build/Step/InstallDir.zig index ecb0959cc7..788d5565a7 100644 --- a/lib/std/Build/Step/InstallDir.zig +++ b/lib/std/Build/Step/InstallDir.zig @@ -68,7 +68,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { var src_dir = src_dir_path.root_dir.handle.openDir(io, src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| { return step.fail("unable to open source directory '{f}': {t}", .{ src_dir_path, err }); }; - defer src_dir.close(); + defer src_dir.close(io); var it = try src_dir.walk(arena); var all_cached = true; next_entry: while (try it.next()) |entry| { diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 28c09e1faf..e66e30cc79 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -851,7 +851,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { .{ file_path, err }, ); }; - defer file.close(); + defer file.close(io); var buf: [1024]u8 = undefined; var file_reader = file.reader(io, &buf); @@ -1111,7 +1111,7 @@ pub fn rerunInFuzzMode( result.writer.writeAll(file_plp.prefix) catch return error.OutOfMemory; const file = try file_path.root_dir.handle.openFile(file_path.subPathOrDot(), .{}); - defer file.close(); + defer file.close(io); var buf: [1024]u8 = undefined; var file_reader = file.reader(io, &buf); @@ -1671,8 +1671,10 @@ fn evalZigTest( options: Step.MakeOptions, fuzz_context: ?FuzzContext, ) !EvalZigTestResult { - const gpa = run.step.owner.allocator; - const arena = run.step.owner.allocator; + const step_owner = run.step.owner; + const gpa = step_owner.allocator; + const arena = step_owner.allocator; + const io = step_owner.graph.io; // We will update this every time a child runs. run.step.result_peak_rss = 0; @@ -1724,7 +1726,7 @@ fn evalZigTest( run.step.result_stderr = try arena.dupe(u8, poller.reader(.stderr).buffered()); // Clean up everything and wait for the child to exit. - child.stdin.?.close(); + child.stdin.?.close(io); child.stdin = null; poller.deinit(); child_killed = true; @@ -1744,7 +1746,7 @@ fn evalZigTest( poller.reader(.stderr).tossBuffered(); // Clean up everything and wait for the child to exit. - child.stdin.?.close(); + child.stdin.?.close(io); child.stdin = null; poller.deinit(); child_killed = true; @@ -2177,7 +2179,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult { child.stdin.?.writeAll(bytes) catch |err| { return run.step.fail("unable to write stdin: {s}", .{@errorName(err)}); }; - child.stdin.?.close(); + child.stdin.?.close(io); child.stdin = null; }, .lazy_path => |lazy_path| { @@ -2185,7 +2187,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult { const file = path.root_dir.handle.openFile(path.subPathOrDot(), .{}) catch |err| { return run.step.fail("unable to open stdin file: {s}", .{@errorName(err)}); }; - defer file.close(); + defer file.close(io); // TODO https://github.com/ziglang/zig/issues/23955 var read_buffer: [1024]u8 = undefined; var file_reader = file.reader(io, &read_buffer); @@ -2204,7 +2206,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult { stdin_writer.err.?, }), }; - child.stdin.?.close(); + child.stdin.?.close(io); child.stdin = null; }, .none => {}, diff --git a/lib/std/Build/Step/WriteFile.zig b/lib/std/Build/Step/WriteFile.zig index 030c7c6811..201b132271 100644 --- a/lib/std/Build/Step/WriteFile.zig +++ b/lib/std/Build/Step/WriteFile.zig @@ -206,7 +206,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { } } - const open_dir_cache = try arena.alloc(fs.Dir, write_file.directories.items.len); + const open_dir_cache = try arena.alloc(Io.Dir, write_file.directories.items.len); var open_dirs_count: usize = 0; defer closeDirs(open_dir_cache[0..open_dirs_count]); @@ -264,7 +264,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { b.cache_root, cache_path, @errorName(err), }); }; - defer cache_dir.close(); + defer cache_dir.close(io); for (write_file.files.items) |file| { if (fs.path.dirname(file.sub_path)) |dirname| { @@ -342,6 +342,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void { try step.writeManifest(&man); } -fn closeDirs(dirs: []fs.Dir) void { - for (dirs) |*d| d.close(); +fn closeDirs(io: Io, dirs: []Io.Dir) void { + var group: Io.Group = .init; + defer group.wait(); + for (dirs) |d| group.async(Io.Dir.close, .{ d, io }); } diff --git a/lib/std/Build/Watch/FsEvents.zig b/lib/std/Build/Watch/FsEvents.zig index 6131663993..59238c8725 100644 --- a/lib/std/Build/Watch/FsEvents.zig +++ b/lib/std/Build/Watch/FsEvents.zig @@ -78,10 +78,10 @@ const ResolvedSymbols = struct { kCFAllocatorUseContext: *const CFAllocatorRef, }; -pub fn init() error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents { +pub fn init(io: Io) error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents { var core_services = std.DynLib.open("/System/Library/Frameworks/CoreServices.framework/CoreServices") catch return error.OpenFrameworkFailed; - errdefer core_services.close(); + errdefer core_services.close(io); var resolved_symbols: ResolvedSymbols = undefined; inline for (@typeInfo(ResolvedSymbols).@"struct".fields) |f| { @@ -102,10 +102,10 @@ pub fn init() error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents { }; } -pub fn deinit(fse: *FsEvents, gpa: Allocator) void { +pub fn deinit(fse: *FsEvents, gpa: Allocator, io: Io) void { dispatch_release(fse.waiting_semaphore); dispatch_release(fse.dispatch_queue); - fse.core_services.close(); + fse.core_services.close(io); gpa.free(fse.watch_roots); fse.watch_paths.deinit(gpa); @@ -487,6 +487,7 @@ const FSEventStreamEventFlags = packed struct(u32) { }; const std = @import("std"); +const Io = std.Io; const assert = std.debug.assert; const Allocator = std.mem.Allocator; const watch_log = std.log.scoped(.watch); diff --git a/lib/std/Build/WebServer.zig b/lib/std/Build/WebServer.zig index 2c865a8889..f91075b444 100644 --- a/lib/std/Build/WebServer.zig +++ b/lib/std/Build/WebServer.zig @@ -129,6 +129,7 @@ pub fn init(opts: Options) WebServer { } pub fn deinit(ws: *WebServer) void { const gpa = ws.gpa; + const io = ws.graph.io; gpa.free(ws.step_names_trailing); gpa.free(ws.step_status_bits); @@ -139,7 +140,7 @@ pub fn deinit(ws: *WebServer) void { gpa.free(ws.time_report_update_times); if (ws.serve_thread) |t| { - if (ws.tcp_server) |*s| s.stream.close(); + if (ws.tcp_server) |*s| s.stream.close(io); t.join(); } if (ws.tcp_server) |*s| s.deinit(); @@ -507,7 +508,7 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons log.err("failed to open '{f}': {s}", .{ path, @errorName(err) }); continue; }; - defer file.close(); + defer file.close(io); const stat = try file.stat(); var read_buffer: [1024]u8 = undefined; var file_reader: Io.File.Reader = .initSize(file.adaptToNewApi(), io, &read_buffer, stat.size); @@ -634,7 +635,7 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim } // Send EOF to stdin. - child.stdin.?.close(); + child.stdin.?.close(io); child.stdin = null; switch (try child.wait()) { diff --git a/lib/std/Io/Dir.zig b/lib/std/Io/Dir.zig index 348950e20a..5a74c8ca72 100644 --- a/lib/std/Io/Dir.zig +++ b/lib/std/Io/Dir.zig @@ -131,7 +131,7 @@ pub const SelectiveWalker = struct { /// After each call to this function, and on deinit(), the memory returned /// from this function becomes invalid. A copy must be made in order to keep /// a reference to the path. - pub fn next(self: *SelectiveWalker) Error!?Walker.Entry { + pub fn next(self: *SelectiveWalker, io: Io) Error!?Walker.Entry { while (self.stack.items.len > 0) { const top = &self.stack.items[self.stack.items.len - 1]; var dirname_len = top.dirname_len; @@ -142,7 +142,7 @@ pub const SelectiveWalker = struct { // likely just fail with the same error. var item = self.stack.pop().?; if (self.stack.items.len != 0) { - item.iter.dir.close(); + item.iter.dir.close(io); } return err; }) |entry| { @@ -164,7 +164,7 @@ pub const SelectiveWalker = struct { } else { var item = self.stack.pop().?; if (self.stack.items.len != 0) { - item.iter.dir.close(); + item.iter.dir.close(io); } } } @@ -172,7 +172,7 @@ pub const SelectiveWalker = struct { } /// Traverses into the directory, continuing walking one level down. - pub fn enter(self: *SelectiveWalker, entry: Walker.Entry) !void { + pub fn enter(self: *SelectiveWalker, io: Io, entry: Walker.Entry) !void { if (entry.kind != .directory) { @branchHint(.cold); return; @@ -184,7 +184,7 @@ pub const SelectiveWalker = struct { else => |e| return e, } }; - errdefer new_dir.close(); + errdefer new_dir.close(io); try self.stack.append(self.allocator, .{ .iter = new_dir.iterateAssumeFirstIteration(), @@ -200,11 +200,11 @@ pub const SelectiveWalker = struct { /// Leaves the current directory, continuing walking one level up. /// If the current entry is a directory entry, then the "current directory" /// will pertain to that entry if `enter` is called before `leave`. - pub fn leave(self: *SelectiveWalker) void { + pub fn leave(self: *SelectiveWalker, io: Io) void { var item = self.stack.pop().?; if (self.stack.items.len != 0) { @branchHint(.likely); - item.iter.dir.close(); + item.iter.dir.close(io); } } }; @@ -558,7 +558,8 @@ pub fn makeDir(dir: Dir, io: Io, sub_path: []const u8, permissions: Permissions) pub const MakePathError = MakeError || StatPathError; -/// Creates parent directories as necessary to ensure `sub_path` exists as a directory. +/// Creates parent directories with default permissions as necessary to ensure +/// `sub_path` exists as a directory. /// /// Returns success if the path already exists and is a directory. /// @@ -579,8 +580,11 @@ pub const MakePathError = MakeError || StatPathError; /// - On other platforms, `..` are not resolved before the path is passed to `mkdirat`, /// meaning a `sub_path` like "first/../second" will create both a `./first` /// and a `./second` directory. -pub fn makePath(dir: Dir, io: Io, sub_path: []const u8, permissions: Permissions) MakePathError!void { - _ = try io.vtable.dirMakePath(io.userdata, dir, sub_path, permissions); +/// +/// See also: +/// * `makePathStatus` +pub fn makePath(dir: Dir, io: Io, sub_path: []const u8) MakePathError!void { + _ = try io.vtable.dirMakePath(io.userdata, dir, sub_path, .default_dir); } pub const MakePathStatus = enum { existed, created }; @@ -593,6 +597,11 @@ pub fn makePathStatus(dir: Dir, io: Io, sub_path: []const u8, permissions: Permi pub const MakeOpenPathError = MakeError || OpenError || StatPathError; +pub const MakeOpenPathOptions = struct { + open_options: OpenOptions = .{}, + permissions: Permissions = .default_dir, +}; + /// Performs the equivalent of `makePath` followed by `openDir`, atomically if possible. /// /// When this operation is canceled, it may leave the file system in a @@ -601,8 +610,8 @@ pub const MakeOpenPathError = MakeError || OpenError || StatPathError; /// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). /// On WASI, `sub_path` should be encoded as valid UTF-8. /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. -pub fn makeOpenPath(dir: Dir, io: Io, sub_path: []const u8, permissions: Permissions, options: OpenOptions) MakeOpenPathError!Dir { - return io.vtable.dirMakeOpenPath(io.userdata, dir, sub_path, permissions, options); +pub fn makeOpenPath(dir: Dir, io: Io, sub_path: []const u8, options: MakeOpenPathOptions) MakeOpenPathError!Dir { + return io.vtable.dirMakeOpenPath(io.userdata, dir, sub_path, options.permissions, options.open_options); } pub const Stat = File.Stat; @@ -1266,10 +1275,10 @@ fn deleteTreeMinStackSizeWithKindHint(parent: Dir, io: Io, sub_path: []const u8, start_over: while (true) { var dir = (try parent.deleteTreeOpenInitialSubpath(io, sub_path, kind_hint)) orelse return; var cleanup_dir_parent: ?Dir = null; - defer if (cleanup_dir_parent) |*d| d.close(); + defer if (cleanup_dir_parent) |*d| d.close(io); var cleanup_dir = true; - defer if (cleanup_dir) dir.close(); + defer if (cleanup_dir) dir.close(io); // Valid use of max_path_bytes because dir_name_buf will only // ever store a single path component that was returned from the @@ -1315,7 +1324,7 @@ fn deleteTreeMinStackSizeWithKindHint(parent: Dir, io: Io, sub_path: []const u8, error.Canceled, => |e| return e, }; - if (cleanup_dir_parent) |*d| d.close(); + if (cleanup_dir_parent) |*d| d.close(io); cleanup_dir_parent = dir; dir = new_dir; const result = dir_name_buf[0..entry.name.len]; @@ -1354,7 +1363,7 @@ fn deleteTreeMinStackSizeWithKindHint(parent: Dir, io: Io, sub_path: []const u8, } // Reached the end of the directory entries, which means we successfully deleted all of them. // Now to remove the directory itself. - dir.close(); + dir.close(io); cleanup_dir = false; if (cleanup_dir_parent) |d| { diff --git a/lib/std/Io/Writer.zig b/lib/std/Io/Writer.zig index f49ef8eb67..5601293cfb 100644 --- a/lib/std/Io/Writer.zig +++ b/lib/std/Io/Writer.zig @@ -2835,7 +2835,7 @@ test "discarding sendFile" { defer tmp_dir.cleanup(); const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); - defer file.close(); + defer file.close(io); var r_buffer: [256]u8 = undefined; var file_writer: std.fs.File.Writer = .init(file, &r_buffer); try file_writer.interface.writeByte('h'); @@ -2857,7 +2857,7 @@ test "allocating sendFile" { defer tmp_dir.cleanup(); const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); - defer file.close(); + defer file.close(io); var r_buffer: [2]u8 = undefined; var file_writer: std.fs.File.Writer = .init(file, &r_buffer); try file_writer.interface.writeAll("abcd"); @@ -2881,7 +2881,7 @@ test sendFileReading { defer tmp_dir.cleanup(); const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); - defer file.close(); + defer file.close(io); var r_buffer: [2]u8 = undefined; var file_writer: std.fs.File.Writer = .init(file, &r_buffer); try file_writer.interface.writeAll("abcd"); diff --git a/lib/std/Io/net/test.zig b/lib/std/Io/net/test.zig index e234a9edde..5818f6c3f7 100644 --- a/lib/std/Io/net/test.zig +++ b/lib/std/Io/net/test.zig @@ -232,8 +232,10 @@ test "listen on an in use port" { fn testClientToHost(allocator: mem.Allocator, name: []const u8, port: u16) anyerror!void { if (builtin.os.tag == .wasi) return error.SkipZigTest; + const io = testing.io; + const connection = try net.tcpConnectToHost(allocator, name, port); - defer connection.close(); + defer connection.close(io); var buf: [100]u8 = undefined; const len = try connection.read(&buf); @@ -244,8 +246,10 @@ fn testClientToHost(allocator: mem.Allocator, name: []const u8, port: u16) anyer fn testClient(addr: net.IpAddress) anyerror!void { if (builtin.os.tag == .wasi) return error.SkipZigTest; + const io = testing.io; + const socket_file = try net.tcpConnectToAddress(addr); - defer socket_file.close(); + defer socket_file.close(io); var buf: [100]u8 = undefined; const len = try socket_file.read(&buf); @@ -330,7 +334,7 @@ test "non-blocking tcp server" { try testing.expectError(error.WouldBlock, accept_err); const socket_file = try net.tcpConnectToAddress(server.socket.address); - defer socket_file.close(); + defer socket_file.close(io); var stream = try server.accept(io); defer stream.close(io); diff --git a/lib/std/Io/test.zig b/lib/std/Io/test.zig index f7965ed14e..9ea2d48ee5 100644 --- a/lib/std/Io/test.zig +++ b/lib/std/Io/test.zig @@ -28,7 +28,7 @@ test "write a file, read it, then delete it" { const tmp_file_name = "temp_test_file.txt"; { var file = try tmp.dir.createFile(tmp_file_name, .{}); - defer file.close(); + defer file.close(io); var file_writer = file.writer(&.{}); const st = &file_writer.interface; @@ -45,7 +45,7 @@ test "write a file, read it, then delete it" { { var file = try tmp.dir.openFile(tmp_file_name, .{}); - defer file.close(); + defer file.close(io); const file_size = try file.getEndPos(); const expected_file_size: u64 = "begin".len + data.len + "end".len; @@ -67,9 +67,11 @@ test "File seek ops" { var tmp = tmpDir(.{}); defer tmp.cleanup(); + const io = testing.io; + const tmp_file_name = "temp_test_file.txt"; var file = try tmp.dir.createFile(tmp_file_name, .{}); - defer file.close(); + defer file.close(io); try file.writeAll(&([_]u8{0x55} ** 8192)); @@ -88,12 +90,14 @@ test "File seek ops" { } test "setEndPos" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); const tmp_file_name = "temp_test_file.txt"; var file = try tmp.dir.createFile(tmp_file_name, .{}); - defer file.close(); + defer file.close(io); // Verify that the file size changes and the file offset is not moved try expect((try file.getEndPos()) == 0); @@ -111,12 +115,14 @@ test "setEndPos" { } test "updateTimes" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); const tmp_file_name = "just_a_temporary_file.txt"; var file = try tmp.dir.createFile(tmp_file_name, .{ .read = true }); - defer file.close(); + defer file.close(io); const stat_old = try file.stat(); // Set atime and mtime to 5s before diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 35b268b349..9f532c3bec 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -7,6 +7,7 @@ const target = builtin.target; const native_os = builtin.os.tag; const std = @import("std.zig"); +const Io = std.Io; const math = std.math; const assert = std.debug.assert; const posix = std.posix; @@ -176,7 +177,7 @@ pub const SetNameError = error{ InvalidWtf8, } || posix.PrctlError || posix.WriteError || std.fs.File.OpenError || std.fmt.BufPrintError; -pub fn setName(self: Thread, name: []const u8) SetNameError!void { +pub fn setName(self: Thread, io: Io, name: []const u8) SetNameError!void { if (name.len > max_name_len) return error.NameTooLong; const name_with_terminator = blk: { @@ -208,7 +209,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void { const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()}); const file = try std.fs.cwd().openFile(path, .{ .mode = .write_only }); - defer file.close(); + defer file.close(io); try file.writeAll(name); return; @@ -325,7 +326,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co const io = threaded.ioBasic(); const file = try std.fs.cwd().openFile(path, .{}); - defer file.close(); + defer file.close(io); var file_reader = file.readerStreaming(io, &.{}); const data_len = file_reader.interface.readSliceShort(buffer_ptr[0 .. max_name_len + 1]) catch |err| switch (err) { diff --git a/lib/std/crypto/Certificate/Bundle.zig b/lib/std/crypto/Certificate/Bundle.zig index 53fb638250..9541e01db5 100644 --- a/lib/std/crypto/Certificate/Bundle.zig +++ b/lib/std/crypto/Certificate/Bundle.zig @@ -181,7 +181,7 @@ pub fn addCertsFromDirPath( sub_dir_path: []const u8, ) AddCertsFromDirPathError!void { var iterable_dir = try dir.openDir(sub_dir_path, .{ .iterate = true }); - defer iterable_dir.close(); + defer iterable_dir.close(io); return addCertsFromDir(cb, gpa, io, iterable_dir); } @@ -194,7 +194,7 @@ pub fn addCertsFromDirPathAbsolute( ) AddCertsFromDirPathError!void { assert(fs.path.isAbsolute(abs_dir_path)); var iterable_dir = try fs.openDirAbsolute(abs_dir_path, .{ .iterate = true }); - defer iterable_dir.close(); + defer iterable_dir.close(io); return addCertsFromDir(cb, gpa, io, now, iterable_dir); } @@ -222,7 +222,7 @@ pub fn addCertsFromFilePathAbsolute( abs_file_path: []const u8, ) AddCertsFromFilePathError!void { var file = try fs.openFileAbsolute(abs_file_path, .{}); - defer file.close(); + defer file.close(io); var file_reader = file.reader(io, &.{}); return addCertsFromFile(cb, gpa, &file_reader, now.toSeconds()); } diff --git a/lib/std/crypto/codecs/asn1/test.zig b/lib/std/crypto/codecs/asn1/test.zig index fe12cba819..ff854fcbde 100644 --- a/lib/std/crypto/codecs/asn1/test.zig +++ b/lib/std/crypto/codecs/asn1/test.zig @@ -75,6 +75,6 @@ test AllTypes { // Use this to update test file. // const dir = try std.fs.cwd().openDir("lib/std/crypto/asn1", .{}); // var file = try dir.createFile(path, .{}); - // defer file.close(); + // defer file.close(io); // try file.writeAll(buf); } diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 7c5993dfcf..0cb96ed593 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1298,7 +1298,7 @@ test printLineFromFile { } { const file = try test_dir.dir.createFile("line_overlaps_page_boundary.zig", .{}); - defer file.close(); + defer file.close(io); const path = try fs.path.join(gpa, &.{ test_dir_path, "line_overlaps_page_boundary.zig" }); defer gpa.free(path); @@ -1317,7 +1317,7 @@ test printLineFromFile { } { const file = try test_dir.dir.createFile("file_ends_on_page_boundary.zig", .{}); - defer file.close(); + defer file.close(io); const path = try fs.path.join(gpa, &.{ test_dir_path, "file_ends_on_page_boundary.zig" }); defer gpa.free(path); @@ -1331,7 +1331,7 @@ test printLineFromFile { } { const file = try test_dir.dir.createFile("very_long_first_line_spanning_multiple_pages.zig", .{}); - defer file.close(); + defer file.close(io); const path = try fs.path.join(gpa, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" }); defer gpa.free(path); @@ -1357,7 +1357,7 @@ test printLineFromFile { } { const file = try test_dir.dir.createFile("file_of_newlines.zig", .{}); - defer file.close(); + defer file.close(io); const path = try fs.path.join(gpa, &.{ test_dir_path, "file_of_newlines.zig" }); defer gpa.free(path); diff --git a/lib/std/debug/ElfFile.zig b/lib/std/debug/ElfFile.zig index e81943ab49..92bcca1bcf 100644 --- a/lib/std/debug/ElfFile.zig +++ b/lib/std/debug/ElfFile.zig @@ -1,5 +1,13 @@ //! A helper type for loading an ELF file and collecting its DWARF debug information, unwind //! information, and symbol table. +const ElfFile = @This(); + +const std = @import("std"); +const Io = std.Io; +const Endian = std.builtin.Endian; +const Dwarf = std.debug.Dwarf; +const Allocator = std.mem.Allocator; +const elf = std.elf; is_64: bool, endian: Endian, @@ -358,10 +366,17 @@ const Section = struct { const Array = std.enums.EnumArray(Section.Id, ?Section); }; -fn loadSeparateDebugFile(arena: Allocator, main_loaded: *LoadInnerResult, opt_crc: ?u32, comptime fmt: []const u8, args: anytype) Allocator.Error!?[]align(std.heap.page_size_min) const u8 { +fn loadSeparateDebugFile( + arena: Allocator, + io: Io, + main_loaded: *LoadInnerResult, + opt_crc: ?u32, + comptime fmt: []const u8, + args: anytype, +) Allocator.Error!?[]align(std.heap.page_size_min) const u8 { const path = try std.fmt.allocPrint(arena, fmt, args); const elf_file = std.fs.cwd().openFile(path, .{}) catch return null; - defer elf_file.close(); + defer elf_file.close(io); const result = loadInner(arena, elf_file, opt_crc) catch |err| switch (err) { error.OutOfMemory => |e| return e, @@ -529,10 +544,3 @@ fn loadInner( .mapped_mem = mapped_mem, }; } - -const std = @import("std"); -const Endian = std.builtin.Endian; -const Dwarf = std.debug.Dwarf; -const ElfFile = @This(); -const Allocator = std.mem.Allocator; -const elf = std.elf; diff --git a/lib/std/debug/Info.zig b/lib/std/debug/Info.zig index 921cd36ab8..9268ca0247 100644 --- a/lib/std/debug/Info.zig +++ b/lib/std/debug/Info.zig @@ -5,19 +5,18 @@ //! Unlike `std.debug.SelfInfo`, this API does not assume the debug information //! in question happens to match the host CPU architecture, OS, or other target //! properties. +const Info = @This(); const std = @import("../std.zig"); +const Io = std.Io; const Allocator = std.mem.Allocator; const Path = std.Build.Cache.Path; const assert = std.debug.assert; const Coverage = std.debug.Coverage; const SourceLocation = std.debug.Coverage.SourceLocation; - const ElfFile = std.debug.ElfFile; const MachOFile = std.debug.MachOFile; -const Info = @This(); - impl: union(enum) { elf: ElfFile, macho: MachOFile, @@ -25,13 +24,23 @@ impl: union(enum) { /// Externally managed, outlives this `Info` instance. coverage: *Coverage, -pub const LoadError = std.fs.File.OpenError || ElfFile.LoadError || MachOFile.Error || std.debug.Dwarf.ScanError || error{ MissingDebugInfo, UnsupportedDebugInfo }; +pub const LoadError = error{ + MissingDebugInfo, + UnsupportedDebugInfo, +} || std.fs.File.OpenError || ElfFile.LoadError || MachOFile.Error || std.debug.Dwarf.ScanError; -pub fn load(gpa: Allocator, path: Path, coverage: *Coverage, format: std.Target.ObjectFormat, arch: std.Target.Cpu.Arch) LoadError!Info { +pub fn load( + gpa: Allocator, + io: Io, + path: Path, + coverage: *Coverage, + format: std.Target.ObjectFormat, + arch: std.Target.Cpu.Arch, +) LoadError!Info { switch (format) { .elf => { var file = try path.root_dir.handle.openFile(path.sub_path, .{}); - defer file.close(); + defer file.close(io); var elf_file: ElfFile = try .load(gpa, file, null, &.none); errdefer elf_file.deinit(gpa); diff --git a/lib/std/debug/MachOFile.zig b/lib/std/debug/MachOFile.zig index 3be1b1daff..3f0f620a90 100644 --- a/lib/std/debug/MachOFile.zig +++ b/lib/std/debug/MachOFile.zig @@ -27,13 +27,13 @@ pub fn deinit(mf: *MachOFile, gpa: Allocator) void { posix.munmap(mf.mapped_memory); } -pub fn load(gpa: Allocator, path: []const u8, arch: std.Target.Cpu.Arch) Error!MachOFile { +pub fn load(gpa: Allocator, io: Io, path: []const u8, arch: std.Target.Cpu.Arch) Error!MachOFile { switch (arch) { .x86_64, .aarch64 => {}, else => unreachable, } - const all_mapped_memory = try mapDebugInfoFile(path); + const all_mapped_memory = try mapDebugInfoFile(io, path); errdefer posix.munmap(all_mapped_memory); // In most cases, the file we just mapped is a Mach-O binary. However, it could be a "universal @@ -239,7 +239,7 @@ pub fn load(gpa: Allocator, path: []const u8, arch: std.Target.Cpu.Arch) Error!M .text_vmaddr = text_vmaddr, }; } -pub fn getDwarfForAddress(mf: *MachOFile, gpa: Allocator, vaddr: u64) !struct { *Dwarf, u64 } { +pub fn getDwarfForAddress(mf: *MachOFile, gpa: Allocator, io: Io, vaddr: u64) !struct { *Dwarf, u64 } { const symbol = Symbol.find(mf.symbols, vaddr) orelse return error.MissingDebugInfo; if (symbol.ofile == Symbol.unknown_ofile) return error.MissingDebugInfo; @@ -254,7 +254,7 @@ pub fn getDwarfForAddress(mf: *MachOFile, gpa: Allocator, vaddr: u64) !struct { const gop = try mf.ofiles.getOrPut(gpa, symbol.ofile); if (!gop.found_existing) { const name = mem.sliceTo(mf.strings[symbol.ofile..], 0); - gop.value_ptr.* = loadOFile(gpa, name); + gop.value_ptr.* = loadOFile(gpa, io, name); } const of = &(gop.value_ptr.* catch |err| return err); @@ -356,7 +356,7 @@ test { _ = Symbol; } -fn loadOFile(gpa: Allocator, o_file_name: []const u8) !OFile { +fn loadOFile(gpa: Allocator, io: Io, o_file_name: []const u8) !OFile { const all_mapped_memory, const mapped_ofile = map: { const open_paren = paren: { if (std.mem.endsWith(u8, o_file_name, ")")) { @@ -365,7 +365,7 @@ fn loadOFile(gpa: Allocator, o_file_name: []const u8) !OFile { } } // Not an archive, just a normal path to a .o file - const m = try mapDebugInfoFile(o_file_name); + const m = try mapDebugInfoFile(io, o_file_name); break :map .{ m, m }; }; @@ -373,7 +373,7 @@ fn loadOFile(gpa: Allocator, o_file_name: []const u8) !OFile { const archive_path = o_file_name[0..open_paren]; const target_name_in_archive = o_file_name[open_paren + 1 .. o_file_name.len - 1]; - const mapped_archive = try mapDebugInfoFile(archive_path); + const mapped_archive = try mapDebugInfoFile(io, archive_path); errdefer posix.munmap(mapped_archive); var ar_reader: Io.Reader = .fixed(mapped_archive); @@ -511,12 +511,12 @@ fn loadOFile(gpa: Allocator, o_file_name: []const u8) !OFile { } /// Uses `mmap` to map the file at `path` into memory. -fn mapDebugInfoFile(path: []const u8) ![]align(std.heap.page_size_min) const u8 { +fn mapDebugInfoFile(io: Io, path: []const u8) ![]align(std.heap.page_size_min) const u8 { const file = std.fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => return error.MissingDebugInfo, else => return error.ReadFailed, }; - defer file.close(); + defer file.close(io); const file_len = std.math.cast( usize, diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig index 59c0b42451..155dac6fb8 100644 --- a/lib/std/debug/SelfInfo/Elf.zig +++ b/lib/std/debug/SelfInfo/Elf.zig @@ -319,14 +319,14 @@ const Module = struct { } /// Assumes we already hold an exclusive lock. - fn getLoadedElf(mod: *Module, gpa: Allocator) Error!*LoadedElf { - if (mod.loaded_elf == null) mod.loaded_elf = loadElf(mod, gpa); + fn getLoadedElf(mod: *Module, gpa: Allocator, io: Io) Error!*LoadedElf { + if (mod.loaded_elf == null) mod.loaded_elf = loadElf(mod, gpa, io); return if (mod.loaded_elf.?) |*elf| elf else |err| err; } - fn loadElf(mod: *Module, gpa: Allocator) Error!LoadedElf { + fn loadElf(mod: *Module, gpa: Allocator, io: Io) Error!LoadedElf { const load_result = if (mod.name.len > 0) res: { var file = std.fs.cwd().openFile(mod.name, .{}) catch return error.MissingDebugInfo; - defer file.close(); + defer file.close(io); break :res std.debug.ElfFile.load(gpa, file, mod.build_id, &.native(mod.name)); } else res: { const path = std.fs.selfExePathAlloc(gpa) catch |err| switch (err) { @@ -335,7 +335,7 @@ const Module = struct { }; defer gpa.free(path); var file = std.fs.cwd().openFile(path, .{}) catch return error.MissingDebugInfo; - defer file.close(); + defer file.close(io); break :res std.debug.ElfFile.load(gpa, file, mod.build_id, &.native(path)); }; diff --git a/lib/std/debug/SelfInfo/MachO.zig b/lib/std/debug/SelfInfo/MachO.zig index dd11b4c8bf..2491cf416c 100644 --- a/lib/std/debug/SelfInfo/MachO.zig +++ b/lib/std/debug/SelfInfo/MachO.zig @@ -615,12 +615,12 @@ test { } /// Uses `mmap` to map the file at `path` into memory. -fn mapDebugInfoFile(path: []const u8) ![]align(std.heap.page_size_min) const u8 { +fn mapDebugInfoFile(io: Io, path: []const u8) ![]align(std.heap.page_size_min) const u8 { const file = std.fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => return error.MissingDebugInfo, else => return error.ReadFailed, }; - defer file.close(); + defer file.close(io); const file_end_pos = file.getEndPos() catch |err| switch (err) { error.Unexpected => |e| return e, diff --git a/lib/std/debug/SelfInfo/Windows.zig b/lib/std/debug/SelfInfo/Windows.zig index 0e923a0f16..557f3901eb 100644 --- a/lib/std/debug/SelfInfo/Windows.zig +++ b/lib/std/debug/SelfInfo/Windows.zig @@ -207,11 +207,11 @@ const Module = struct { file: fs.File, section_handle: windows.HANDLE, section_view: []const u8, - fn deinit(mf: *const MappedFile) void { + fn deinit(mf: *const MappedFile, io: Io) void { const process_handle = windows.GetCurrentProcess(); assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @constCast(mf.section_view.ptr)) == .SUCCESS); windows.CloseHandle(mf.section_handle); - mf.file.close(); + mf.file.close(io); } }; @@ -447,7 +447,7 @@ const Module = struct { error.FileNotFound, error.IsDir => break :pdb null, else => return error.ReadFailed, }; - errdefer pdb_file.close(); + errdefer pdb_file.close(io); const pdb_reader = try arena.create(Io.File.Reader); pdb_reader.* = pdb_file.reader(io, try arena.alloc(u8, 4096)); diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index a3490ed7db..c91056b0ab 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -1,10 +1,12 @@ -const std = @import("std.zig"); const builtin = @import("builtin"); +const native_os = builtin.os.tag; + +const std = @import("std.zig"); +const Io = std.Io; const mem = std.mem; const testing = std.testing; const elf = std.elf; const windows = std.os.windows; -const native_os = builtin.os.tag; const posix = std.posix; /// Cross-platform dynamic library loading and symbol lookup. @@ -38,8 +40,8 @@ pub const DynLib = struct { } /// Trusts the file. - pub fn close(self: *DynLib) void { - return self.inner.close(); + pub fn close(self: *DynLib, io: Io) void { + return self.inner.close(io); } pub fn lookup(self: *DynLib, comptime T: type, name: [:0]const u8) ?T { @@ -155,23 +157,23 @@ pub const ElfDynLib = struct { dt_gnu_hash: *elf.gnu_hash.Header, }; - fn openPath(path: []const u8) !std.fs.Dir { + fn openPath(path: []const u8, io: Io) !std.fs.Dir { if (path.len == 0) return error.NotDir; var parts = std.mem.tokenizeScalar(u8, path, '/'); var parent = if (path[0] == '/') try std.fs.cwd().openDir("/", .{}) else std.fs.cwd(); while (parts.next()) |part| { const child = try parent.openDir(part, .{}); - parent.close(); + parent.close(io); parent = child; } return parent; } - fn resolveFromSearchPath(search_path: []const u8, file_name: []const u8, delim: u8) ?posix.fd_t { + fn resolveFromSearchPath(io: Io, search_path: []const u8, file_name: []const u8, delim: u8) ?posix.fd_t { var paths = std.mem.tokenizeScalar(u8, search_path, delim); while (paths.next()) |p| { var dir = openPath(p) catch continue; - defer dir.close(); + defer dir.close(io); const fd = posix.openat(dir.fd, file_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true, @@ -181,9 +183,9 @@ pub const ElfDynLib = struct { return null; } - fn resolveFromParent(dir_path: []const u8, file_name: []const u8) ?posix.fd_t { + fn resolveFromParent(io: Io, dir_path: []const u8, file_name: []const u8) ?posix.fd_t { var dir = std.fs.cwd().openDir(dir_path, .{}) catch return null; - defer dir.close(); + defer dir.close(io); return posix.openat(dir.fd, file_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true, @@ -195,7 +197,7 @@ pub const ElfDynLib = struct { // - DT_RPATH of the calling binary is not used as a search path // - DT_RUNPATH of the calling binary is not used as a search path // - /etc/ld.so.cache is not read - fn resolveFromName(path_or_name: []const u8) !posix.fd_t { + fn resolveFromName(io: Io, path_or_name: []const u8) !posix.fd_t { // If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname if (std.mem.findScalarPos(u8, path_or_name, 0, '/')) |_| { return posix.open(path_or_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); @@ -206,21 +208,21 @@ pub const ElfDynLib = struct { std.os.linux.getegid() == std.os.linux.getgid()) { if (posix.getenvZ("LD_LIBRARY_PATH")) |ld_library_path| { - if (resolveFromSearchPath(ld_library_path, path_or_name, ':')) |fd| { + if (resolveFromSearchPath(io, ld_library_path, path_or_name, ':')) |fd| { return fd; } } } // Lastly the directories /lib and /usr/lib are searched (in this exact order) - if (resolveFromParent("/lib", path_or_name)) |fd| return fd; - if (resolveFromParent("/usr/lib", path_or_name)) |fd| return fd; + if (resolveFromParent(io, "/lib", path_or_name)) |fd| return fd; + if (resolveFromParent(io, "/usr/lib", path_or_name)) |fd| return fd; return error.FileNotFound; } /// Trusts the file. Malicious file will be able to execute arbitrary code. - pub fn open(path: []const u8) Error!ElfDynLib { - const fd = try resolveFromName(path); + pub fn open(io: Io, path: []const u8) Error!ElfDynLib { + const fd = try resolveFromName(io, path); defer posix.close(fd); const file: std.fs.File = .{ .handle = fd }; diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 72fab9c7c2..9472e5d2a5 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -227,7 +227,7 @@ pub fn deleteFileAbsolute(absolute_path: []const u8) Dir.DeleteFileError!void { /// On Windows, `absolute_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). /// On WASI, `absolute_path` should be encoded as valid UTF-8. /// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. -pub fn deleteTreeAbsolute(absolute_path: []const u8) !void { +pub fn deleteTreeAbsolute(io: Io, absolute_path: []const u8) !void { assert(path.isAbsolute(absolute_path)); const dirname = path.dirname(absolute_path) orelse return error{ /// Attempt to remove the root file system path. @@ -236,7 +236,7 @@ pub fn deleteTreeAbsolute(absolute_path: []const u8) !void { }.CannotDeleteRootDirectory; var dir = try cwd().openDir(dirname, .{}); - defer dir.close(); + defer dir.close(io); return dir.deleteTree(path.basename(absolute_path)); } diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 52baa0699d..15b8e9b558 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -178,6 +178,8 @@ fn setupSymlinkAbsolute(target: []const u8, link: []const u8, flags: SymLinkFlag } test "Dir.readLink" { + const io = testing.io; + try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { // Create some targets @@ -208,7 +210,7 @@ test "Dir.readLink" { const parent_file = ".." ++ fs.path.sep_str ++ "target.txt"; const canonical_parent_file = try ctx.toCanonicalPathSep(parent_file); var subdir = try ctx.dir.makeOpenPath("subdir", .{}); - defer subdir.close(); + defer subdir.close(io); try setupSymlink(subdir, canonical_parent_file, "relative-link.txt", .{}); try testReadLink(subdir, canonical_parent_file, "relative-link.txt"); if (builtin.os.tag == .windows) { @@ -268,6 +270,8 @@ fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void } test "File.stat on a File that is a symlink returns Kind.sym_link" { + const io = testing.io; + // This test requires getting a file descriptor of a symlink which // is not possible on all targets switch (builtin.target.os.tag) { @@ -302,7 +306,7 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" { .SecurityDescriptor = null, .SecurityQualityOfService = null, }; - var io: windows.IO_STATUS_BLOCK = undefined; + var io_status_block: windows.IO_STATUS_BLOCK = undefined; const rc = windows.ntdll.NtCreateFile( &handle, .{ @@ -317,7 +321,7 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" { }, }, &attr, - &io, + &io_status_block, null, .{ .NORMAL = true }, .VALID_FLAGS, @@ -352,7 +356,7 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" { }, else => unreachable, }; - defer symlink.close(); + defer symlink.close(io); const stat = try symlink.stat(); try testing.expectEqual(File.Kind.sym_link, stat.kind); @@ -361,6 +365,8 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" { } test "openDir" { + const io = testing.io; + try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { const allocator = ctx.arena.allocator(); @@ -370,7 +376,7 @@ test "openDir" { for ([_][]const u8{ "", ".", ".." }) |sub_path| { const dir_path = try fs.path.join(allocator, &.{ subdir_path, sub_path }); var dir = try ctx.dir.openDir(dir_path, .{}); - defer dir.close(); + defer dir.close(io); } } }.impl); @@ -393,6 +399,8 @@ test "openDirAbsolute" { if (native_os == .wasi) return error.SkipZigTest; if (native_os == .openbsd) return error.SkipZigTest; + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -404,7 +412,7 @@ test "openDirAbsolute" { // Can open sub_path var tmp_sub = try fs.openDirAbsolute(sub_path, .{}); - defer tmp_sub.close(); + defer tmp_sub.close(io); const sub_ino = (try tmp_sub.stat()).inode; @@ -414,7 +422,7 @@ test "openDirAbsolute" { defer testing.allocator.free(dir_path); var dir = try fs.openDirAbsolute(dir_path, .{}); - defer dir.close(); + defer dir.close(io); const ino = (try dir.stat()).inode; try testing.expectEqual(tmp_ino, ino); @@ -426,7 +434,7 @@ test "openDirAbsolute" { defer testing.allocator.free(dir_path); var dir = try fs.openDirAbsolute(dir_path, .{}); - defer dir.close(); + defer dir.close(io); const ino = (try dir.stat()).inode; try testing.expectEqual(sub_ino, ino); @@ -438,7 +446,7 @@ test "openDirAbsolute" { defer testing.allocator.free(dir_path); var dir = try fs.openDirAbsolute(dir_path, .{}); - defer dir.close(); + defer dir.close(io); const ino = (try dir.stat()).inode; try testing.expectEqual(tmp_ino, ino); @@ -446,13 +454,15 @@ test "openDirAbsolute" { } test "openDir cwd parent '..'" { + const io = testing.io; + var dir = fs.cwd().openDir("..", .{}) catch |err| { if (native_os == .wasi and err == error.PermissionDenied) { return; // This is okay. WASI disallows escaping from the fs sandbox } return err; }; - defer dir.close(); + defer dir.close(io); } test "openDir non-cwd parent '..'" { @@ -461,14 +471,16 @@ test "openDir non-cwd parent '..'" { else => {}, } + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); var subdir = try tmp.dir.makeOpenPath("subdir", .{}); - defer subdir.close(); + defer subdir.close(io); var dir = try subdir.openDir("..", .{}); - defer dir.close(); + defer dir.close(io); const expected_path = try tmp.dir.realpathAlloc(testing.allocator, "."); defer testing.allocator.free(expected_path); @@ -516,12 +528,14 @@ test "readLinkAbsolute" { } test "Dir.Iterator" { + const io = testing.io; + var tmp_dir = tmpDir(.{ .iterate = true }); defer tmp_dir.cleanup(); // First, create a couple of entries to iterate over. const file = try tmp_dir.dir.createFile("some_file", .{}); - file.close(); + file.close(io); try tmp_dir.dir.makeDir("some_dir"); @@ -546,6 +560,8 @@ test "Dir.Iterator" { } test "Dir.Iterator many entries" { + const io = testing.io; + var tmp_dir = tmpDir(.{ .iterate = true }); defer tmp_dir.cleanup(); @@ -555,7 +571,7 @@ test "Dir.Iterator many entries" { while (i < num) : (i += 1) { const name = try std.fmt.bufPrint(&buf, "{}", .{i}); const file = try tmp_dir.dir.createFile(name, .{}); - file.close(); + file.close(io); } var arena = ArenaAllocator.init(testing.allocator); @@ -581,12 +597,14 @@ test "Dir.Iterator many entries" { } test "Dir.Iterator twice" { + const io = testing.io; + var tmp_dir = tmpDir(.{ .iterate = true }); defer tmp_dir.cleanup(); // First, create a couple of entries to iterate over. const file = try tmp_dir.dir.createFile("some_file", .{}); - file.close(); + file.close(io); try tmp_dir.dir.makeDir("some_dir"); @@ -614,12 +632,14 @@ test "Dir.Iterator twice" { } test "Dir.Iterator reset" { + const io = testing.io; + var tmp_dir = tmpDir(.{ .iterate = true }); defer tmp_dir.cleanup(); // First, create a couple of entries to iterate over. const file = try tmp_dir.dir.createFile("some_file", .{}); - file.close(); + file.close(io); try tmp_dir.dir.makeDir("some_dir"); @@ -650,12 +670,14 @@ test "Dir.Iterator reset" { } test "Dir.Iterator but dir is deleted during iteration" { + const io = testing.io; + var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); // Create directory and setup an iterator for it var subdir = try tmp.dir.makeOpenPath("subdir", .{ .iterate = true }); - defer subdir.close(); + defer subdir.close(io); var iterator = subdir.iterate(); @@ -742,11 +764,13 @@ test "Dir.realpath smoke test" { } test "readFileAlloc" { + const io = testing.io; + var tmp_dir = tmpDir(.{}); defer tmp_dir.cleanup(); var file = try tmp_dir.dir.createFile("test_file", .{ .read = true }); - defer file.close(); + defer file.close(io); const buf1 = try tmp_dir.dir.readFileAlloc("test_file", testing.allocator, .limited(1024)); defer testing.allocator.free(buf1); @@ -815,10 +839,12 @@ test "statFile on dangling symlink" { test "directory operations on files" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; + const test_file_name = try ctx.transformPath("test_file"); var file = try ctx.dir.createFile(test_file_name, .{ .read = true }); - file.close(); + file.close(io); try testing.expectError(error.PathAlreadyExists, ctx.dir.makeDir(test_file_name)); try testing.expectError(error.NotDir, ctx.dir.openDir(test_file_name, .{})); @@ -833,7 +859,7 @@ test "directory operations on files" { file = try ctx.dir.openFile(test_file_name, .{}); const stat = try file.stat(); try testing.expectEqual(File.Kind.file, stat.kind); - file.close(); + file.close(io); } }.impl); } @@ -842,6 +868,8 @@ test "file operations on directories" { // TODO: fix this test on FreeBSD. https://github.com/ziglang/zig/issues/1759 if (native_os == .freebsd) return error.SkipZigTest; + const io = testing.io; + try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { const test_dir_name = try ctx.transformPath("test_dir"); @@ -869,7 +897,7 @@ test "file operations on directories" { if (native_os == .wasi and builtin.link_libc) { // wasmtime unexpectedly succeeds here, see https://github.com/ziglang/zig/issues/20747 const handle = try ctx.dir.openFile(test_dir_name, .{ .mode = .read_write }); - handle.close(); + handle.close(io); } else { // Note: The `.mode = .read_write` is necessary to ensure the error occurs on all platforms. // TODO: Add a read-only test as well, see https://github.com/ziglang/zig/issues/5732 @@ -883,21 +911,23 @@ test "file operations on directories" { // ensure the directory still exists as a sanity check var dir = try ctx.dir.openDir(test_dir_name, .{}); - dir.close(); + dir.close(io); } }.impl); } test "makeOpenPath parent dirs do not exist" { + const io = testing.io; + var tmp_dir = tmpDir(.{}); defer tmp_dir.cleanup(); var dir = try tmp_dir.dir.makeOpenPath("root_dir/parent_dir/some_dir", .{}); - dir.close(); + dir.close(io); // double check that the full directory structure was created var dir_verification = try tmp_dir.dir.openDir("root_dir/parent_dir/some_dir", .{}); - dir_verification.close(); + dir_verification.close(io); } test "deleteDir" { @@ -924,6 +954,7 @@ test "deleteDir" { test "Dir.rename files" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; // Rename on Windows can hit intermittent AccessDenied errors // when certain conditions are true about the host system. // For now, skip this test when the path type is UNC to avoid them. @@ -939,13 +970,13 @@ test "Dir.rename files" { const test_file_name = try ctx.transformPath("test_file"); const renamed_test_file_name = try ctx.transformPath("test_file_renamed"); var file = try ctx.dir.createFile(test_file_name, .{ .read = true }); - file.close(); + file.close(io); try ctx.dir.rename(test_file_name, renamed_test_file_name); // Ensure the file was renamed try testing.expectError(error.FileNotFound, ctx.dir.openFile(test_file_name, .{})); file = try ctx.dir.openFile(renamed_test_file_name, .{}); - file.close(); + file.close(io); // Rename to self succeeds try ctx.dir.rename(renamed_test_file_name, renamed_test_file_name); @@ -953,12 +984,12 @@ test "Dir.rename files" { // Rename to existing file succeeds const existing_file_path = try ctx.transformPath("existing_file"); var existing_file = try ctx.dir.createFile(existing_file_path, .{ .read = true }); - existing_file.close(); + existing_file.close(io); try ctx.dir.rename(renamed_test_file_name, existing_file_path); try testing.expectError(error.FileNotFound, ctx.dir.openFile(renamed_test_file_name, .{})); file = try ctx.dir.openFile(existing_file_path, .{}); - file.close(); + file.close(io); } }.impl); } @@ -966,6 +997,8 @@ test "Dir.rename files" { test "Dir.rename directories" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; + // Rename on Windows can hit intermittent AccessDenied errors // when certain conditions are true about the host system. // For now, skip this test when the path type is UNC to avoid them. @@ -985,8 +1018,8 @@ test "Dir.rename directories" { // Put a file in the directory var file = try dir.createFile("test_file", .{ .read = true }); - file.close(); - dir.close(); + file.close(io); + dir.close(io); const test_dir_renamed_again_path = try ctx.transformPath("test_dir_renamed_again"); try ctx.dir.rename(test_dir_renamed_path, test_dir_renamed_again_path); @@ -995,8 +1028,8 @@ test "Dir.rename directories" { try testing.expectError(error.FileNotFound, ctx.dir.openDir(test_dir_renamed_path, .{})); dir = try ctx.dir.openDir(test_dir_renamed_again_path, .{}); file = try dir.openFile("test_file", .{}); - file.close(); - dir.close(); + file.close(io); + dir.close(io); } }.impl); } @@ -1007,6 +1040,8 @@ test "Dir.rename directory onto empty dir" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; + const test_dir_path = try ctx.transformPath("test_dir"); const target_dir_path = try ctx.transformPath("target_dir_path"); @@ -1017,7 +1052,7 @@ test "Dir.rename directory onto empty dir" { // Ensure the directory was renamed try testing.expectError(error.FileNotFound, ctx.dir.openDir(test_dir_path, .{})); var dir = try ctx.dir.openDir(target_dir_path, .{}); - dir.close(); + dir.close(io); } }.impl); } @@ -1028,6 +1063,7 @@ test "Dir.rename directory onto non-empty dir" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const test_dir_path = try ctx.transformPath("test_dir"); const target_dir_path = try ctx.transformPath("target_dir_path"); @@ -1035,15 +1071,15 @@ test "Dir.rename directory onto non-empty dir" { var target_dir = try ctx.dir.makeOpenPath(target_dir_path, .{}); var file = try target_dir.createFile("test_file", .{ .read = true }); - file.close(); - target_dir.close(); + file.close(io); + target_dir.close(io); // Rename should fail with PathAlreadyExists if target_dir is non-empty try testing.expectError(error.PathAlreadyExists, ctx.dir.rename(test_dir_path, target_dir_path)); // Ensure the directory was not renamed var dir = try ctx.dir.openDir(test_dir_path, .{}); - dir.close(); + dir.close(io); } }.impl); } @@ -1054,11 +1090,12 @@ test "Dir.rename file <-> dir" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const test_file_path = try ctx.transformPath("test_file"); const test_dir_path = try ctx.transformPath("test_dir"); var file = try ctx.dir.createFile(test_file_path, .{ .read = true }); - file.close(); + file.close(io); try ctx.dir.makeDir(test_dir_path); try testing.expectError(error.IsDir, ctx.dir.rename(test_file_path, test_dir_path)); try testing.expectError(error.NotDir, ctx.dir.rename(test_dir_path, test_file_path)); @@ -1067,6 +1104,8 @@ test "Dir.rename file <-> dir" { } test "rename" { + const io = testing.io; + var tmp_dir1 = tmpDir(.{}); defer tmp_dir1.cleanup(); @@ -1077,19 +1116,21 @@ test "rename" { const test_file_name = "test_file"; const renamed_test_file_name = "test_file_renamed"; var file = try tmp_dir1.dir.createFile(test_file_name, .{ .read = true }); - file.close(); + file.close(io); try fs.rename(tmp_dir1.dir, test_file_name, tmp_dir2.dir, renamed_test_file_name); // ensure the file was renamed try testing.expectError(error.FileNotFound, tmp_dir1.dir.openFile(test_file_name, .{})); file = try tmp_dir2.dir.openFile(renamed_test_file_name, .{}); - file.close(); + file.close(io); } test "renameAbsolute" { if (native_os == .wasi) return error.SkipZigTest; if (native_os == .openbsd) return error.SkipZigTest; + const io = testing.io; + var tmp_dir = tmpDir(.{}); defer tmp_dir.cleanup(); @@ -1109,7 +1150,7 @@ test "renameAbsolute" { const test_file_name = "test_file"; const renamed_test_file_name = "test_file_renamed"; var file = try tmp_dir.dir.createFile(test_file_name, .{ .read = true }); - file.close(); + file.close(io); try fs.renameAbsolute( try fs.path.join(allocator, &.{ base_path, test_file_name }), try fs.path.join(allocator, &.{ base_path, renamed_test_file_name }), @@ -1120,7 +1161,7 @@ test "renameAbsolute" { file = try tmp_dir.dir.openFile(renamed_test_file_name, .{}); const stat = try file.stat(); try testing.expectEqual(File.Kind.file, stat.kind); - file.close(); + file.close(io); // Renaming directories const test_dir_name = "test_dir"; @@ -1134,14 +1175,16 @@ test "renameAbsolute" { // ensure the directory was renamed try testing.expectError(error.FileNotFound, tmp_dir.dir.openDir(test_dir_name, .{})); var dir = try tmp_dir.dir.openDir(renamed_test_dir_name, .{}); - dir.close(); + dir.close(io); } test "openSelfExe" { if (native_os == .wasi) return error.SkipZigTest; + const io = testing.io; + const self_exe_file = try std.fs.openSelfExe(.{}); - self_exe_file.close(); + self_exe_file.close(io); } test "selfExePath" { @@ -1155,13 +1198,15 @@ test "selfExePath" { } test "deleteTree does not follow symlinks" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); try tmp.dir.makePath("b"); { var a = try tmp.dir.makeOpenPath("a", .{}); - defer a.close(); + defer a.close(io); try setupSymlink(a, "../b", "b", .{ .is_directory = true }); } @@ -1257,27 +1302,31 @@ test "makePath but sub_path contains pre-existing file" { try testing.expectError(error.NotDir, tmp.dir.makePath("foo/bar/baz")); } -fn expectDir(dir: Dir, path: []const u8) !void { +fn expectDir(io: Io, dir: Dir, path: []const u8) !void { var d = try dir.openDir(path, .{}); - d.close(); + d.close(io); } test "makepath existing directories" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); try tmp.dir.makeDir("A"); var tmpA = try tmp.dir.openDir("A", .{}); - defer tmpA.close(); + defer tmpA.close(io); try tmpA.makeDir("B"); const testPath = "A" ++ fs.path.sep_str ++ "B" ++ fs.path.sep_str ++ "C"; try tmp.dir.makePath(testPath); - try expectDir(tmp.dir, testPath); + try expectDir(io, tmp.dir, testPath); } test "makepath through existing valid symlink" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -1286,10 +1335,12 @@ test "makepath through existing valid symlink" { try tmp.dir.makePath("working-symlink" ++ fs.path.sep_str ++ "in-realfolder"); - try expectDir(tmp.dir, "realfolder" ++ fs.path.sep_str ++ "in-realfolder"); + try expectDir(io, tmp.dir, "realfolder" ++ fs.path.sep_str ++ "in-realfolder"); } test "makepath relative walks" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -1305,21 +1356,23 @@ test "makepath relative walks" { .windows => { // On Windows, .. is resolved before passing the path to NtCreateFile, // meaning everything except `first/C` drops out. - try expectDir(tmp.dir, "first" ++ fs.path.sep_str ++ "C"); + try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "C"); try testing.expectError(error.FileNotFound, tmp.dir.access("second", .{})); try testing.expectError(error.FileNotFound, tmp.dir.access("third", .{})); }, else => { - try expectDir(tmp.dir, "first" ++ fs.path.sep_str ++ "A"); - try expectDir(tmp.dir, "first" ++ fs.path.sep_str ++ "B"); - try expectDir(tmp.dir, "first" ++ fs.path.sep_str ++ "C"); - try expectDir(tmp.dir, "second"); - try expectDir(tmp.dir, "third"); + try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "A"); + try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "B"); + try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "C"); + try expectDir(io, tmp.dir, "second"); + try expectDir(io, tmp.dir, "third"); }, } } test "makepath ignores '.'" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -1337,14 +1390,14 @@ test "makepath ignores '.'" { try tmp.dir.makePath(dotPath); - try expectDir(tmp.dir, expectedPath); + try expectDir(io, tmp.dir, expectedPath); } -fn testFilenameLimits(iterable_dir: Dir, maxed_filename: []const u8) !void { +fn testFilenameLimits(io: Io, iterable_dir: Dir, maxed_filename: []const u8) !void { // setup, create a dir and a nested file both with maxed filenames, and walk the dir { var maxed_dir = try iterable_dir.makeOpenPath(maxed_filename, .{}); - defer maxed_dir.close(); + defer maxed_dir.close(io); try maxed_dir.writeFile(.{ .sub_path = maxed_filename, .data = "" }); @@ -1364,6 +1417,8 @@ fn testFilenameLimits(iterable_dir: Dir, maxed_filename: []const u8) !void { } test "max file name component lengths" { + const io = testing.io; + var tmp = tmpDir(.{ .iterate = true }); defer tmp.cleanup(); @@ -1371,16 +1426,16 @@ test "max file name component lengths" { // U+FFFF is the character with the largest code point that is encoded as a single // UTF-16 code unit, so Windows allows for NAME_MAX of them. const maxed_windows_filename = ("\u{FFFF}".*) ** windows.NAME_MAX; - try testFilenameLimits(tmp.dir, &maxed_windows_filename); + try testFilenameLimits(io, tmp.dir, &maxed_windows_filename); } else if (native_os == .wasi) { // On WASI, the maxed filename depends on the host OS, so in order for this test to // work on any host, we need to use a length that will work for all platforms // (i.e. the minimum max_name_bytes of all supported platforms). const maxed_wasi_filename = [_]u8{'1'} ** 255; - try testFilenameLimits(tmp.dir, &maxed_wasi_filename); + try testFilenameLimits(io, tmp.dir, &maxed_wasi_filename); } else { const maxed_ascii_filename = [_]u8{'1'} ** std.fs.max_name_bytes; - try testFilenameLimits(tmp.dir, &maxed_ascii_filename); + try testFilenameLimits(io, tmp.dir, &maxed_ascii_filename); } } @@ -1399,7 +1454,7 @@ test "writev, readv" { var read_vecs: [2][]u8 = .{ &buf2, &buf1 }; var src_file = try tmp.dir.createFile("test.txt", .{ .read = true }); - defer src_file.close(); + defer src_file.close(io); var writer = src_file.writerStreaming(&.{}); @@ -1429,7 +1484,7 @@ test "pwritev, preadv" { var read_vecs: [2][]u8 = .{ &buf2, &buf1 }; var src_file = try tmp.dir.createFile("test.txt", .{ .read = true }); - defer src_file.close(); + defer src_file.close(io); var writer = src_file.writer(&.{}); @@ -1459,7 +1514,7 @@ test "setEndPos" { const file_name = "afile.txt"; try tmp.dir.writeFile(.{ .sub_path = file_name, .data = "ninebytes" }); const f = try tmp.dir.openFile(file_name, .{ .mode = .read_write }); - defer f.close(); + defer f.close(io); const initial_size = try f.getEndPos(); var buffer: [32]u8 = undefined; @@ -1522,21 +1577,21 @@ test "sendfile" { try tmp.dir.makePath("os_test_tmp"); var dir = try tmp.dir.openDir("os_test_tmp", .{}); - defer dir.close(); + defer dir.close(io); const line1 = "line1\n"; const line2 = "second line\n"; var vecs = [_][]const u8{ line1, line2 }; var src_file = try dir.createFile("sendfile1.txt", .{ .read = true }); - defer src_file.close(); + defer src_file.close(io); { var fw = src_file.writer(&.{}); try fw.interface.writeVecAll(&vecs); } var dest_file = try dir.createFile("sendfile2.txt", .{ .read = true }); - defer dest_file.close(); + defer dest_file.close(io); const header1 = "header1\n"; const header2 = "second header\n"; @@ -1569,15 +1624,15 @@ test "sendfile with buffered data" { try tmp.dir.makePath("os_test_tmp"); var dir = try tmp.dir.openDir("os_test_tmp", .{}); - defer dir.close(); + defer dir.close(io); var src_file = try dir.createFile("sendfile1.txt", .{ .read = true }); - defer src_file.close(); + defer src_file.close(io); try src_file.writeAll("AAAABBBB"); var dest_file = try dir.createFile("sendfile2.txt", .{ .read = true }); - defer dest_file.close(); + defer dest_file.close(io); var src_buffer: [32]u8 = undefined; var file_reader = src_file.reader(io, &src_buffer); @@ -1659,10 +1714,11 @@ test "open file with exclusive nonblocking lock twice" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const filename = try ctx.transformPath("file_nonblocking_lock_test.txt"); const file1 = try ctx.dir.createFile(filename, .{ .lock = .exclusive, .lock_nonblocking = true }); - defer file1.close(); + defer file1.close(io); const file2 = ctx.dir.createFile(filename, .{ .lock = .exclusive, .lock_nonblocking = true }); try testing.expectError(error.WouldBlock, file2); @@ -1675,10 +1731,11 @@ test "open file with shared and exclusive nonblocking lock" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const filename = try ctx.transformPath("file_nonblocking_lock_test.txt"); const file1 = try ctx.dir.createFile(filename, .{ .lock = .shared, .lock_nonblocking = true }); - defer file1.close(); + defer file1.close(io); const file2 = ctx.dir.createFile(filename, .{ .lock = .exclusive, .lock_nonblocking = true }); try testing.expectError(error.WouldBlock, file2); @@ -1691,10 +1748,11 @@ test "open file with exclusive and shared nonblocking lock" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const filename = try ctx.transformPath("file_nonblocking_lock_test.txt"); const file1 = try ctx.dir.createFile(filename, .{ .lock = .exclusive, .lock_nonblocking = true }); - defer file1.close(); + defer file1.close(io); const file2 = ctx.dir.createFile(filename, .{ .lock = .shared, .lock_nonblocking = true }); try testing.expectError(error.WouldBlock, file2); @@ -1707,10 +1765,11 @@ test "open file with exclusive lock twice, make sure second lock waits" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const filename = try ctx.transformPath("file_lock_test.txt"); const file = try ctx.dir.createFile(filename, .{ .lock = .exclusive }); - errdefer file.close(); + errdefer file.close(io); const S = struct { fn checkFn(dir: *fs.Dir, path: []const u8, started: *std.Thread.ResetEvent, locked: *std.Thread.ResetEvent) !void { @@ -1718,7 +1777,7 @@ test "open file with exclusive lock twice, make sure second lock waits" { const file1 = try dir.createFile(path, .{ .lock = .exclusive }); locked.set(); - file1.close(); + file1.close(io); } }; @@ -1739,7 +1798,7 @@ test "open file with exclusive lock twice, make sure second lock waits" { try testing.expectError(error.Timeout, locked.timedWait(10 * std.time.ns_per_ms)); // Release the file lock which should unlock the thread to lock it and set the locked event. - file.close(); + file.close(io); locked.wait(); } }.impl); @@ -1748,6 +1807,8 @@ test "open file with exclusive lock twice, make sure second lock waits" { test "open file with exclusive nonblocking lock twice (absolute paths)" { if (native_os == .wasi) return error.SkipZigTest; + const io = testing.io; + var random_bytes: [12]u8 = undefined; std.crypto.random.bytes(&random_bytes); @@ -1774,18 +1835,19 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" { .lock = .exclusive, .lock_nonblocking = true, }); - file1.close(); + file1.close(io); try testing.expectError(error.WouldBlock, file2); } test "read from locked file" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const filename = try ctx.transformPath("read_lock_file_test.txt"); { const f = try ctx.dir.createFile(filename, .{ .read = true }); - defer f.close(); + defer f.close(io); var buffer: [1]u8 = undefined; _ = try f.read(&buffer); } @@ -1794,9 +1856,9 @@ test "read from locked file" { .read = true, .lock = .exclusive, }); - defer f.close(); + defer f.close(io); const f2 = try ctx.dir.openFile(filename, .{}); - defer f2.close(); + defer f2.close(io); var buffer: [1]u8 = undefined; if (builtin.os.tag == .windows) { try std.testing.expectError(error.LockViolation, f2.read(&buffer)); @@ -1809,6 +1871,8 @@ test "read from locked file" { } test "walker" { + const io = testing.io; + var tmp = tmpDir(.{ .iterate = true }); defer tmp.cleanup(); @@ -1857,13 +1921,15 @@ test "walker" { }; // make sure that the entry.dir is the containing dir var entry_dir = try entry.dir.openDir(entry.basename, .{}); - defer entry_dir.close(); + defer entry_dir.close(io); num_walked += 1; } try testing.expectEqual(expected_paths.kvs.len, num_walked); } test "selective walker, skip entries that start with ." { + const io = testing.io; + var tmp = tmpDir(.{ .iterate = true }); defer tmp.cleanup(); @@ -1923,7 +1989,7 @@ test "selective walker, skip entries that start with ." { // make sure that the entry.dir is the containing dir var entry_dir = try entry.dir.openDir(entry.basename, .{}); - defer entry_dir.close(); + defer entry_dir.close(io); num_walked += 1; } try testing.expectEqual(expected_paths.kvs.len, num_walked); @@ -1968,16 +2034,16 @@ test "'.' and '..' in fs.Dir functions" { try ctx.dir.makeDir(subdir_path); try ctx.dir.access(subdir_path, .{}); var created_subdir = try ctx.dir.openDir(subdir_path, .{}); - created_subdir.close(); + created_subdir.close(io); const created_file = try ctx.dir.createFile(file_path, .{}); - created_file.close(); + created_file.close(io); try ctx.dir.access(file_path, .{}); try ctx.dir.copyFile(file_path, ctx.dir, copy_path, .{}); try ctx.dir.rename(copy_path, rename_path); const renamed_file = try ctx.dir.openFile(rename_path, .{}); - renamed_file.close(); + renamed_file.close(io); try ctx.dir.deleteFile(rename_path); try ctx.dir.writeFile(.{ .sub_path = update_path, .data = "something" }); @@ -1994,6 +2060,8 @@ test "'.' and '..' in absolute functions" { if (native_os == .wasi) return error.SkipZigTest; if (native_os == .openbsd) return error.SkipZigTest; + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -2007,11 +2075,11 @@ test "'.' and '..' in absolute functions" { try fs.makeDirAbsolute(subdir_path); try fs.accessAbsolute(subdir_path, .{}); var created_subdir = try fs.openDirAbsolute(subdir_path, .{}); - created_subdir.close(); + created_subdir.close(io); const created_file_path = try fs.path.join(allocator, &.{ subdir_path, "../file" }); const created_file = try fs.createFileAbsolute(created_file_path, .{}); - created_file.close(); + created_file.close(io); try fs.accessAbsolute(created_file_path, .{}); const copied_file_path = try fs.path.join(allocator, &.{ subdir_path, "../copy" }); @@ -2019,7 +2087,7 @@ test "'.' and '..' in absolute functions" { const renamed_file_path = try fs.path.join(allocator, &.{ subdir_path, "../rename" }); try fs.renameAbsolute(copied_file_path, renamed_file_path); const renamed_file = try fs.openFileAbsolute(renamed_file_path, .{}); - renamed_file.close(); + renamed_file.close(io); try fs.deleteFileAbsolute(renamed_file_path); try fs.deleteDirAbsolute(subdir_path); @@ -2029,11 +2097,13 @@ test "chmod" { if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); const file = try tmp.dir.createFile("test_file", .{ .mode = 0o600 }); - defer file.close(); + defer file.close(io); try testing.expectEqual(@as(File.Mode, 0o600), (try file.stat()).mode & 0o7777); try file.chmod(0o644); @@ -2041,7 +2111,7 @@ test "chmod" { try tmp.dir.makeDir("test_dir"); var dir = try tmp.dir.openDir("test_dir", .{ .iterate = true }); - defer dir.close(); + defer dir.close(io); try dir.chmod(0o700); try testing.expectEqual(@as(File.Mode, 0o700), (try dir.stat()).mode & 0o7777); @@ -2051,17 +2121,19 @@ test "chown" { if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); const file = try tmp.dir.createFile("test_file", .{}); - defer file.close(); + defer file.close(io); try file.chown(null, null); try tmp.dir.makeDir("test_dir"); var dir = try tmp.dir.openDir("test_dir", .{ .iterate = true }); - defer dir.close(); + defer dir.close(io); try dir.chown(null, null); } @@ -2157,7 +2229,7 @@ test "read file non vectored" { const contents = "hello, world!\n"; const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); - defer file.close(); + defer file.close(io); { var file_writer: std.fs.File.Writer = .init(file, &.{}); try file_writer.interface.writeAll(contents); @@ -2189,7 +2261,7 @@ test "seek keeping partial buffer" { const contents = "0123456789"; const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); - defer file.close(); + defer file.close(io); { var file_writer: std.fs.File.Writer = .init(file, &.{}); try file_writer.interface.writeAll(contents); @@ -2231,7 +2303,7 @@ test "seekBy" { try tmp_dir.dir.writeFile(.{ .sub_path = "blah.txt", .data = "let's test seekBy" }); const f = try tmp_dir.dir.openFile("blah.txt", .{ .mode = .read_only }); - defer f.close(); + defer f.close(io); var reader = f.readerStreaming(io, &.{}); try reader.seekBy(2); @@ -2250,7 +2322,7 @@ test "seekTo flushes buffered data" { const contents = "data"; const file = try tmp.dir.createFile("seek.bin", .{ .read = true }); - defer file.close(); + defer file.close(io); { var buf: [16]u8 = undefined; var file_writer = std.fs.File.writer(file, &buf); @@ -2277,9 +2349,9 @@ test "File.Writer sendfile with buffered contents" { { try tmp_dir.dir.writeFile(.{ .sub_path = "a", .data = "bcd" }); const in = try tmp_dir.dir.openFile("a", .{}); - defer in.close(); + defer in.close(io); const out = try tmp_dir.dir.createFile("b", .{}); - defer out.close(); + defer out.close(io); var in_buf: [2]u8 = undefined; var in_r = in.reader(io, &in_buf); @@ -2294,7 +2366,7 @@ test "File.Writer sendfile with buffered contents" { } var check = try tmp_dir.dir.openFile("b", .{}); - defer check.close(); + defer check.close(io); var check_buf: [4]u8 = undefined; var check_r = check.reader(io, &check_buf); try testing.expectEqualStrings("abcd", try check_r.interface.take(4)); diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index 10ab23f476..7f8c9827ef 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -1473,6 +1473,8 @@ pub const ConnectUnixError = Allocator.Error || std.posix.SocketError || error{N /// /// This function is threadsafe. pub fn connectUnix(client: *Client, path: []const u8) ConnectUnixError!*Connection { + const io = client.io; + if (client.connection_pool.findConnection(.{ .host = path, .port = 0, @@ -1485,7 +1487,7 @@ pub fn connectUnix(client: *Client, path: []const u8) ConnectUnixError!*Connecti conn.* = .{ .data = undefined }; const stream = try Io.net.connectUnixSocket(path); - errdefer stream.close(); + errdefer stream.close(io); conn.data = .{ .stream = stream, diff --git a/lib/std/os/linux/IoUring.zig b/lib/std/os/linux/IoUring.zig index c927dab376..e4a5bd3738 100644 --- a/lib/std/os/linux/IoUring.zig +++ b/lib/std/os/linux/IoUring.zig @@ -1,13 +1,16 @@ const IoUring = @This(); -const std = @import("std"); + const builtin = @import("builtin"); +const is_linux = builtin.os.tag == .linux; + +const std = @import("std"); +const Io = std.Io; const assert = std.debug.assert; const mem = std.mem; const net = std.Io.net; const posix = std.posix; const linux = std.os.linux; const testing = std.testing; -const is_linux = builtin.os.tag == .linux; const page_size_min = std.heap.page_size_min; fd: linux.fd_t = -1, @@ -1975,6 +1978,8 @@ test "readv" { test "writev/fsync/readv" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(4, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -1987,7 +1992,7 @@ test "writev/fsync/readv" { const path = "test_io_uring_writev_fsync_readv"; const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); - defer file.close(); + defer file.close(io); const fd = file.handle; const buffer_write = [_]u8{42} ** 128; @@ -2045,6 +2050,8 @@ test "writev/fsync/readv" { test "write/read" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(2, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2056,7 +2063,7 @@ test "write/read" { defer tmp.cleanup(); const path = "test_io_uring_write_read"; const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); - defer file.close(); + defer file.close(io); const fd = file.handle; const buffer_write = [_]u8{97} ** 20; @@ -2092,6 +2099,8 @@ test "write/read" { test "splice/read" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(4, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2102,12 +2111,12 @@ test "splice/read" { var tmp = std.testing.tmpDir(.{}); const path_src = "test_io_uring_splice_src"; const file_src = try tmp.dir.createFile(path_src, .{ .read = true, .truncate = true }); - defer file_src.close(); + defer file_src.close(io); const fd_src = file_src.handle; const path_dst = "test_io_uring_splice_dst"; const file_dst = try tmp.dir.createFile(path_dst, .{ .read = true, .truncate = true }); - defer file_dst.close(); + defer file_dst.close(io); const fd_dst = file_dst.handle; const buffer_write = [_]u8{97} ** 20; @@ -2163,6 +2172,8 @@ test "splice/read" { test "write_fixed/read_fixed" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(2, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2175,7 +2186,7 @@ test "write_fixed/read_fixed" { const path = "test_io_uring_write_read_fixed"; const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); - defer file.close(); + defer file.close(io); const fd = file.handle; var raw_buffers: [2][11]u8 = undefined; @@ -2282,6 +2293,8 @@ test "openat" { test "close" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(1, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2294,7 +2307,7 @@ test "close" { const path = "test_io_uring_close"; const file = try tmp.dir.createFile(path, .{}); - errdefer file.close(); + errdefer file.close(io); const sqe_close = try ring.close(0x44444444, file.handle); try testing.expectEqual(linux.IORING_OP.CLOSE, sqe_close.opcode); @@ -2313,6 +2326,8 @@ test "close" { test "accept/connect/send/recv" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2321,7 +2336,7 @@ test "accept/connect/send/recv" { defer ring.deinit(); const socket_test_harness = try createSocketTestHarness(&ring); - defer socket_test_harness.close(); + defer socket_test_harness.close(io); const buffer_send = [_]u8{ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 }; var buffer_recv = [_]u8{ 0, 1, 0, 1, 0 }; @@ -2573,6 +2588,8 @@ test "timeout_remove" { test "accept/connect/recv/link_timeout" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2581,7 +2598,7 @@ test "accept/connect/recv/link_timeout" { defer ring.deinit(); const socket_test_harness = try createSocketTestHarness(&ring); - defer socket_test_harness.close(); + defer socket_test_harness.close(io); var buffer_recv = [_]u8{ 0, 1, 0, 1, 0 }; @@ -2622,6 +2639,8 @@ test "accept/connect/recv/link_timeout" { test "fallocate" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(1, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2634,7 +2653,7 @@ test "fallocate" { const path = "test_io_uring_fallocate"; const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); - defer file.close(); + defer file.close(io); try testing.expectEqual(@as(u64, 0), (try file.stat()).size); @@ -2668,6 +2687,8 @@ test "fallocate" { test "statx" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(1, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2679,7 +2700,7 @@ test "statx" { defer tmp.cleanup(); const path = "test_io_uring_statx"; const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); - defer file.close(); + defer file.close(io); try testing.expectEqual(@as(u64, 0), (try file.stat()).size); @@ -2725,6 +2746,8 @@ test "statx" { test "accept/connect/recv/cancel" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2733,7 +2756,7 @@ test "accept/connect/recv/cancel" { defer ring.deinit(); const socket_test_harness = try createSocketTestHarness(&ring); - defer socket_test_harness.close(); + defer socket_test_harness.close(io); var buffer_recv = [_]u8{ 0, 1, 0, 1, 0 }; @@ -2929,6 +2952,8 @@ test "shutdown" { test "renameat" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(1, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2945,7 +2970,7 @@ test "renameat" { // Write old file with data const old_file = try tmp.dir.createFile(old_path, .{ .truncate = true, .mode = 0o666 }); - defer old_file.close(); + defer old_file.close(io); try old_file.writeAll("hello"); // Submit renameat @@ -2987,6 +3012,8 @@ test "renameat" { test "unlinkat" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(1, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -3002,7 +3029,7 @@ test "unlinkat" { // Write old file with data const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); - defer file.close(); + defer file.close(io); // Submit unlinkat @@ -3083,6 +3110,8 @@ test "mkdirat" { test "symlinkat" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(1, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -3097,7 +3126,7 @@ test "symlinkat" { const link_path = "test_io_uring_symlinkat_link"; const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); - defer file.close(); + defer file.close(io); // Submit symlinkat @@ -3131,6 +3160,8 @@ test "symlinkat" { test "linkat" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(1, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -3147,7 +3178,7 @@ test "linkat" { // Write file with data const first_file = try tmp.dir.createFile(first_path, .{ .truncate = true, .mode = 0o666 }); - defer first_file.close(); + defer first_file.close(io); try first_file.writeAll("hello"); // Submit linkat @@ -3407,6 +3438,8 @@ test "remove_buffers" { test "provide_buffers: accept/connect/send/recv" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -3443,7 +3476,7 @@ test "provide_buffers: accept/connect/send/recv" { } const socket_test_harness = try createSocketTestHarness(&ring); - defer socket_test_harness.close(); + defer socket_test_harness.close(io); // Do 4 send on the socket @@ -3696,6 +3729,8 @@ test "accept multishot" { test "accept/connect/send_zc/recv" { try skipKernelLessThan(.{ .major = 6, .minor = 0, .patch = 0 }); + const io = testing.io; + var ring = IoUring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -3704,7 +3739,7 @@ test "accept/connect/send_zc/recv" { defer ring.deinit(); const socket_test_harness = try createSocketTestHarness(&ring); - defer socket_test_harness.close(); + defer socket_test_harness.close(io); const buffer_send = [_]u8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe }; var buffer_recv = [_]u8{0} ** 10; @@ -4105,6 +4140,8 @@ inline fn skipKernelLessThan(required: std.SemanticVersion) !void { test BufferGroup { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + // Init IoUring var ring = IoUring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, @@ -4132,7 +4169,7 @@ test BufferGroup { // Create client/server fds const fds = try createSocketTestHarness(&ring); - defer fds.close(); + defer fds.close(io); const data = [_]u8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe }; // Client sends data @@ -4170,6 +4207,8 @@ test BufferGroup { test "ring mapped buffers recv" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -4196,7 +4235,7 @@ test "ring mapped buffers recv" { // create client/server fds const fds = try createSocketTestHarness(&ring); - defer fds.close(); + defer fds.close(io); // for random user_data in sqe/cqe var Rnd = std.Random.DefaultPrng.init(std.testing.random_seed); @@ -4259,6 +4298,8 @@ test "ring mapped buffers recv" { test "ring mapped buffers multishot recv" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -4285,7 +4326,7 @@ test "ring mapped buffers multishot recv" { // create client/server fds const fds = try createSocketTestHarness(&ring); - defer fds.close(); + defer fds.close(io); // for random user_data in sqe/cqe var Rnd = std.Random.DefaultPrng.init(std.testing.random_seed); diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig index 7399907477..39606ddfac 100644 --- a/lib/std/os/linux/test.zig +++ b/lib/std/os/linux/test.zig @@ -12,12 +12,14 @@ const fs = std.fs; test "fallocate" { if (builtin.cpu.arch.isMIPS64() and (builtin.abi == .gnuabin32 or builtin.abi == .muslabin32)) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/30220 + const io = std.testing.io; + var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); const path = "test_fallocate"; const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); - defer file.close(); + defer file.close(io); try expect((try file.stat()).size == 0); @@ -77,12 +79,14 @@ test "timer" { } test "statx" { + const io = std.testing.io; + var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); const tmp_file_name = "just_a_temporary_file.txt"; var file = try tmp.dir.createFile(tmp_file_name, .{}); - defer file.close(); + defer file.close(io); var buf: linux.Statx = undefined; switch (linux.errno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, .BASIC_STATS, &buf))) { @@ -111,12 +115,14 @@ test "user and group ids" { } test "fadvise" { + const io = std.testing.io; + var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); const tmp_file_name = "temp_posix_fadvise.txt"; var file = try tmp.dir.createFile(tmp_file_name, .{}); - defer file.close(); + defer file.close(io); var buf: [2048]u8 = undefined; try file.writeAll(&buf); diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index 3bb5e64c73..8889e50ea3 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -148,6 +148,8 @@ test "linkat with different directories" { else => return error.SkipZigTest, } + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -163,10 +165,10 @@ test "linkat with different directories" { try posix.linkat(tmp.dir.fd, target_name, subdir.fd, link_name, 0); const efd = try tmp.dir.openFile(target_name, .{}); - defer efd.close(); + defer efd.close(io); const nfd = try subdir.openFile(link_name, .{}); - defer nfd.close(); + defer nfd.close(io); { const eino, _ = try getLinkInfo(efd.handle); @@ -381,6 +383,8 @@ test "mmap" { if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -413,7 +417,7 @@ test "mmap" { // Create a file used for testing mmap() calls with a file descriptor { const file = try tmp.dir.createFile(test_out_file, .{}); - defer file.close(); + defer file.close(io); var stream = file.writer(&.{}); @@ -426,7 +430,7 @@ test "mmap" { // Map the whole file { const file = try tmp.dir.openFile(test_out_file, .{}); - defer file.close(); + defer file.close(io); const data = try posix.mmap( null, @@ -451,7 +455,7 @@ test "mmap" { // Map the upper half of the file { const file = try tmp.dir.openFile(test_out_file, .{}); - defer file.close(); + defer file.close(io); const data = try posix.mmap( null, @@ -476,13 +480,15 @@ test "fcntl" { if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); const test_out_file = "os_tmp_test"; const file = try tmp.dir.createFile(test_out_file, .{}); - defer file.close(); + defer file.close(io); // Note: The test assumes createFile opens the file with CLOEXEC { @@ -526,12 +532,14 @@ test "fsync" { else => return error.SkipZigTest, } + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); const test_out_file = "os_tmp_test"; const file = try tmp.dir.createFile(test_out_file, .{}); - defer file.close(); + defer file.close(io); try posix.fsync(file.handle); try posix.fdatasync(file.handle); @@ -646,22 +654,24 @@ test "dup & dup2" { else => return error.SkipZigTest, } + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); { var file = try tmp.dir.createFile("os_dup_test", .{}); - defer file.close(); + defer file.close(io); var duped = std.fs.File{ .handle = try posix.dup(file.handle) }; - defer duped.close(); + defer duped.close(io); try duped.writeAll("dup"); // Tests aren't run in parallel so using the next fd shouldn't be an issue. const new_fd = duped.handle + 1; try posix.dup2(file.handle, new_fd); var dup2ed = std.fs.File{ .handle = new_fd }; - defer dup2ed.close(); + defer dup2ed.close(io); try dup2ed.writeAll("dup2"); } @@ -687,11 +697,13 @@ test "getppid" { test "writev longer than IOV_MAX" { if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); var file = try tmp.dir.createFile("pwritev", .{}); - defer file.close(); + defer file.close(io); const iovecs = [_]posix.iovec_const{.{ .base = "a", .len = 1 }} ** (posix.IOV_MAX + 1); const amt = try file.writev(&iovecs); @@ -709,12 +721,14 @@ test "POSIX file locking with fcntl" { return error.SkipZigTest; } + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); // Create a temporary lock file var file = try tmp.dir.createFile("lock", .{ .read = true }); - defer file.close(); + defer file.close(io); try file.setEndPos(2); const fd = file.handle; @@ -905,21 +919,25 @@ test "timerfd" { } test "isatty" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); var file = try tmp.dir.createFile("foo", .{}); - defer file.close(); + defer file.close(io); try expectEqual(posix.isatty(file.handle), false); } test "pread with empty buffer" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); var file = try tmp.dir.createFile("pread_empty", .{ .read = true }); - defer file.close(); + defer file.close(io); const bytes = try a.alloc(u8, 0); defer a.free(bytes); @@ -929,11 +947,13 @@ test "pread with empty buffer" { } test "write with empty buffer" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); var file = try tmp.dir.createFile("write_empty", .{}); - defer file.close(); + defer file.close(io); const bytes = try a.alloc(u8, 0); defer a.free(bytes); @@ -943,11 +963,13 @@ test "write with empty buffer" { } test "pwrite with empty buffer" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); var file = try tmp.dir.createFile("pwrite_empty", .{}); - defer file.close(); + defer file.close(io); const bytes = try a.alloc(u8, 0); defer a.free(bytes); diff --git a/lib/std/process.zig b/lib/std/process.zig index a0a26c766f..a8dede6ad4 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -1,12 +1,14 @@ -const std = @import("std.zig"); const builtin = @import("builtin"); +const native_os = builtin.os.tag; + +const std = @import("std.zig"); +const Io = std.Io; const fs = std.fs; const mem = std.mem; const math = std.math; -const Allocator = mem.Allocator; +const Allocator = std.mem.Allocator; const assert = std.debug.assert; const testing = std.testing; -const native_os = builtin.os.tag; const posix = std.posix; const windows = std.os.windows; const unicode = std.unicode; @@ -1571,9 +1573,9 @@ pub fn getUserInfo(name: []const u8) !UserInfo { /// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else /// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`. -pub fn posixGetUserInfo(name: []const u8) !UserInfo { +pub fn posixGetUserInfo(io: Io, name: []const u8) !UserInfo { const file = try std.fs.openFileAbsolute("/etc/passwd", .{}); - defer file.close(); + defer file.close(io); var buffer: [4096]u8 = undefined; var file_reader = file.reader(&buffer); return posixGetUserInfoPasswdStream(name, &file_reader.interface) catch |err| switch (err) { diff --git a/lib/std/process/Child.zig b/lib/std/process/Child.zig index 6dad72df44..be3026ff10 100644 --- a/lib/std/process/Child.zig +++ b/lib/std/process/Child.zig @@ -4,6 +4,7 @@ const builtin = @import("builtin"); const native_os = builtin.os.tag; const std = @import("../std.zig"); +const Io = std.Io; const unicode = std.unicode; const fs = std.fs; const process = std.process; @@ -277,17 +278,17 @@ pub fn spawnAndWait(self: *ChildProcess) SpawnError!Term { } /// Forcibly terminates child process and then cleans up all resources. -pub fn kill(self: *ChildProcess) !Term { +pub fn kill(self: *ChildProcess, io: Io) !Term { if (native_os == .windows) { - return self.killWindows(1); + return self.killWindows(io, 1); } else { - return self.killPosix(); + return self.killPosix(io); } } -pub fn killWindows(self: *ChildProcess, exit_code: windows.UINT) !Term { +pub fn killWindows(self: *ChildProcess, io: Io, exit_code: windows.UINT) !Term { if (self.term) |term| { - self.cleanupStreams(); + self.cleanupStreams(io); return term; } @@ -303,20 +304,20 @@ pub fn killWindows(self: *ChildProcess, exit_code: windows.UINT) !Term { }, else => return err, }; - try self.waitUnwrappedWindows(); + try self.waitUnwrappedWindows(io); return self.term.?; } -pub fn killPosix(self: *ChildProcess) !Term { +pub fn killPosix(self: *ChildProcess, io: Io) !Term { if (self.term) |term| { - self.cleanupStreams(); + self.cleanupStreams(io); return term; } posix.kill(self.id, posix.SIG.TERM) catch |err| switch (err) { error.ProcessNotFound => return error.AlreadyTerminated, else => return err, }; - self.waitUnwrappedPosix(); + self.waitUnwrappedPosix(io); return self.term.?; } @@ -354,15 +355,15 @@ pub fn waitForSpawn(self: *ChildProcess) SpawnError!void { } /// Blocks until child process terminates and then cleans up all resources. -pub fn wait(self: *ChildProcess) WaitError!Term { +pub fn wait(self: *ChildProcess, io: Io) WaitError!Term { try self.waitForSpawn(); // report spawn errors if (self.term) |term| { - self.cleanupStreams(); + self.cleanupStreams(io); return term; } switch (native_os) { - .windows => try self.waitUnwrappedWindows(), - else => self.waitUnwrappedPosix(), + .windows => try self.waitUnwrappedWindows(io), + else => self.waitUnwrappedPosix(io), } self.id = undefined; return self.term.?; @@ -474,7 +475,7 @@ pub fn run(args: struct { }; } -fn waitUnwrappedWindows(self: *ChildProcess) WaitError!void { +fn waitUnwrappedWindows(self: *ChildProcess, io: Io) WaitError!void { const result = windows.WaitForSingleObjectEx(self.id, windows.INFINITE, false); self.term = @as(SpawnError!Term, x: { @@ -492,11 +493,11 @@ fn waitUnwrappedWindows(self: *ChildProcess) WaitError!void { posix.close(self.id); posix.close(self.thread_handle); - self.cleanupStreams(); + self.cleanupStreams(io); return result; } -fn waitUnwrappedPosix(self: *ChildProcess) void { +fn waitUnwrappedPosix(self: *ChildProcess, io: Io) void { const res: posix.WaitPidResult = res: { if (self.request_resource_usage_statistics) { switch (native_os) { @@ -527,7 +528,7 @@ fn waitUnwrappedPosix(self: *ChildProcess) void { break :res posix.waitpid(self.id, 0); }; const status = res.status; - self.cleanupStreams(); + self.cleanupStreams(io); self.handleWaitResult(status); } @@ -535,17 +536,17 @@ fn handleWaitResult(self: *ChildProcess, status: u32) void { self.term = statusToTerm(status); } -fn cleanupStreams(self: *ChildProcess) void { +fn cleanupStreams(self: *ChildProcess, io: Io) void { if (self.stdin) |*stdin| { - stdin.close(); + stdin.close(io); self.stdin = null; } if (self.stdout) |*stdout| { - stdout.close(); + stdout.close(io); self.stdout = null; } if (self.stderr) |*stderr| { - stderr.close(); + stderr.close(io); self.stderr = null; } } diff --git a/lib/std/tar.zig b/lib/std/tar.zig index bf96aed35c..d861314fec 100644 --- a/lib/std/tar.zig +++ b/lib/std/tar.zig @@ -16,6 +16,7 @@ //! pax reference: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13 const std = @import("std"); +const Io = std.Io; const assert = std.debug.assert; const testing = std.testing; @@ -302,7 +303,7 @@ pub const FileKind = enum { /// Iterator over entries in the tar file represented by reader. pub const Iterator = struct { - reader: *std.Io.Reader, + reader: *Io.Reader, diagnostics: ?*Diagnostics = null, // buffers for heeader and file attributes @@ -328,7 +329,7 @@ pub const Iterator = struct { /// Iterates over files in tar archive. /// `next` returns each file in tar archive. - pub fn init(reader: *std.Io.Reader, options: Options) Iterator { + pub fn init(reader: *Io.Reader, options: Options) Iterator { return .{ .reader = reader, .diagnostics = options.diagnostics, @@ -473,7 +474,7 @@ pub const Iterator = struct { return null; } - pub fn streamRemaining(it: *Iterator, file: File, w: *std.Io.Writer) std.Io.Reader.StreamError!void { + pub fn streamRemaining(it: *Iterator, file: File, w: *Io.Writer) Io.Reader.StreamError!void { try it.reader.streamExact64(w, file.size); it.unread_file_bytes = 0; } @@ -499,14 +500,14 @@ const pax_max_size_attr_len = 64; pub const PaxIterator = struct { size: usize, // cumulative size of all pax attributes - reader: *std.Io.Reader, + reader: *Io.Reader, const Self = @This(); const Attribute = struct { kind: PaxAttributeKind, len: usize, // length of the attribute value - reader: *std.Io.Reader, // reader positioned at value start + reader: *Io.Reader, // reader positioned at value start // Copies pax attribute value into destination buffer. // Must be called with destination buffer of size at least Attribute.len. @@ -573,13 +574,13 @@ pub const PaxIterator = struct { } // Checks that each record ends with new line. - fn validateAttributeEnding(reader: *std.Io.Reader) !void { + fn validateAttributeEnding(reader: *Io.Reader) !void { if (try reader.takeByte() != '\n') return error.PaxInvalidAttributeEnd; } }; /// Saves tar file content to the file systems. -pub fn pipeToFileSystem(dir: std.fs.Dir, reader: *std.Io.Reader, options: PipeOptions) !void { +pub fn pipeToFileSystem(io: Io, dir: Io.Dir, reader: *Io.Reader, options: PipeOptions) !void { var file_name_buffer: [std.fs.max_path_bytes]u8 = undefined; var link_name_buffer: [std.fs.max_path_bytes]u8 = undefined; var file_contents_buffer: [1024]u8 = undefined; @@ -610,7 +611,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: *std.Io.Reader, options: PipeOp }, .file => { if (createDirAndFile(dir, file_name, fileMode(file.mode, options))) |fs_file| { - defer fs_file.close(); + defer fs_file.close(io); var file_writer = fs_file.writer(&file_contents_buffer); try it.streamRemaining(file, &file_writer.interface); try file_writer.interface.flush(); @@ -637,7 +638,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: *std.Io.Reader, options: PipeOp } } -fn createDirAndFile(dir: std.fs.Dir, file_name: []const u8, mode: std.fs.File.Mode) !std.fs.File { +fn createDirAndFile(dir: Io.Dir, file_name: []const u8, mode: Io.File.Mode) !Io.File { const fs_file = dir.createFile(file_name, .{ .exclusive = true, .mode = mode }) catch |err| { if (err == error.FileNotFound) { if (std.fs.path.dirname(file_name)) |dir_name| { @@ -651,7 +652,7 @@ fn createDirAndFile(dir: std.fs.Dir, file_name: []const u8, mode: std.fs.File.Mo } // Creates a symbolic link at path `file_name` which points to `link_name`. -fn createDirAndSymlink(dir: std.fs.Dir, link_name: []const u8, file_name: []const u8) !void { +fn createDirAndSymlink(dir: Io.Dir, link_name: []const u8, file_name: []const u8) !void { dir.symLink(link_name, file_name, .{}) catch |err| { if (err == error.FileNotFound) { if (std.fs.path.dirname(file_name)) |dir_name| { @@ -783,7 +784,7 @@ test PaxIterator { var buffer: [1024]u8 = undefined; outer: for (cases) |case| { - var reader: std.Io.Reader = .fixed(case.data); + var reader: Io.Reader = .fixed(case.data); var it: PaxIterator = .{ .size = case.data.len, .reader = &reader, @@ -874,13 +875,15 @@ test "header parse mode" { } test "create file and symlink" { + const io = testing.io; + var root = testing.tmpDir(.{}); defer root.cleanup(); var file = try createDirAndFile(root.dir, "file1", default_mode); - file.close(); + file.close(io); file = try createDirAndFile(root.dir, "a/b/c/file2", default_mode); - file.close(); + file.close(io); createDirAndSymlink(root.dir, "a/b/c/file2", "symlink1") catch |err| { // On Windows when developer mode is not enabled @@ -892,7 +895,7 @@ test "create file and symlink" { // Danglink symlnik, file created later try createDirAndSymlink(root.dir, "../../../g/h/i/file4", "j/k/l/symlink3"); file = try createDirAndFile(root.dir, "g/h/i/file4", default_mode); - file.close(); + file.close(io); } test Iterator { @@ -916,7 +919,7 @@ test Iterator { // example/empty/ const data = @embedFile("tar/testdata/example.tar"); - var reader: std.Io.Reader = .fixed(data); + var reader: Io.Reader = .fixed(data); // User provided buffers to the iterator var file_name_buffer: [std.fs.max_path_bytes]u8 = undefined; @@ -942,7 +945,7 @@ test Iterator { .file => { try testing.expectEqualStrings("example/a/file", file.name); var buf: [16]u8 = undefined; - var w: std.Io.Writer = .fixed(&buf); + var w: Io.Writer = .fixed(&buf); try it.streamRemaining(file, &w); try testing.expectEqualStrings("content\n", w.buffered()); }, @@ -955,6 +958,7 @@ test Iterator { } test pipeToFileSystem { + const io = testing.io; // Example tar file is created from this tree structure: // $ tree example // example @@ -975,14 +979,14 @@ test pipeToFileSystem { // example/empty/ const data = @embedFile("tar/testdata/example.tar"); - var reader: std.Io.Reader = .fixed(data); + var reader: Io.Reader = .fixed(data); var tmp = testing.tmpDir(.{ .follow_symlinks = false }); defer tmp.cleanup(); const dir = tmp.dir; // Save tar from reader to the file system `dir` - pipeToFileSystem(dir, &reader, .{ + pipeToFileSystem(io, dir, &reader, .{ .mode_mode = .ignore, .strip_components = 1, .exclude_empty_directories = true, @@ -1005,8 +1009,9 @@ test pipeToFileSystem { } test "pipeToFileSystem root_dir" { + const io = testing.io; const data = @embedFile("tar/testdata/example.tar"); - var reader: std.Io.Reader = .fixed(data); + var reader: Io.Reader = .fixed(data); // with strip_components = 1 { @@ -1015,7 +1020,7 @@ test "pipeToFileSystem root_dir" { var diagnostics: Diagnostics = .{ .allocator = testing.allocator }; defer diagnostics.deinit(); - pipeToFileSystem(tmp.dir, &reader, .{ + pipeToFileSystem(io, tmp.dir, &reader, .{ .strip_components = 1, .diagnostics = &diagnostics, }) catch |err| { @@ -1037,7 +1042,7 @@ test "pipeToFileSystem root_dir" { var diagnostics: Diagnostics = .{ .allocator = testing.allocator }; defer diagnostics.deinit(); - pipeToFileSystem(tmp.dir, &reader, .{ + pipeToFileSystem(io, tmp.dir, &reader, .{ .strip_components = 0, .diagnostics = &diagnostics, }) catch |err| { @@ -1053,43 +1058,46 @@ test "pipeToFileSystem root_dir" { } test "findRoot with single file archive" { + const io = testing.io; const data = @embedFile("tar/testdata/22752.tar"); - var reader: std.Io.Reader = .fixed(data); + var reader: Io.Reader = .fixed(data); var tmp = testing.tmpDir(.{}); defer tmp.cleanup(); var diagnostics: Diagnostics = .{ .allocator = testing.allocator }; defer diagnostics.deinit(); - try pipeToFileSystem(tmp.dir, &reader, .{ .diagnostics = &diagnostics }); + try pipeToFileSystem(io, tmp.dir, &reader, .{ .diagnostics = &diagnostics }); try testing.expectEqualStrings("", diagnostics.root_dir); } test "findRoot without explicit root dir" { + const io = testing.io; const data = @embedFile("tar/testdata/19820.tar"); - var reader: std.Io.Reader = .fixed(data); + var reader: Io.Reader = .fixed(data); var tmp = testing.tmpDir(.{}); defer tmp.cleanup(); var diagnostics: Diagnostics = .{ .allocator = testing.allocator }; defer diagnostics.deinit(); - try pipeToFileSystem(tmp.dir, &reader, .{ .diagnostics = &diagnostics }); + try pipeToFileSystem(io, tmp.dir, &reader, .{ .diagnostics = &diagnostics }); try testing.expectEqualStrings("root", diagnostics.root_dir); } test "pipeToFileSystem strip_components" { + const io = testing.io; const data = @embedFile("tar/testdata/example.tar"); - var reader: std.Io.Reader = .fixed(data); + var reader: Io.Reader = .fixed(data); var tmp = testing.tmpDir(.{ .follow_symlinks = false }); defer tmp.cleanup(); var diagnostics: Diagnostics = .{ .allocator = testing.allocator }; defer diagnostics.deinit(); - pipeToFileSystem(tmp.dir, &reader, .{ + pipeToFileSystem(io, tmp.dir, &reader, .{ .strip_components = 3, .diagnostics = &diagnostics, }) catch |err| { @@ -1110,10 +1118,10 @@ fn normalizePath(bytes: []u8) []u8 { return bytes; } -const default_mode = std.fs.File.default_mode; +const default_mode = Io.File.default_mode; // File system mode based on tar header mode and mode_mode options. -fn fileMode(mode: u32, options: PipeOptions) std.fs.File.Mode { +fn fileMode(mode: u32, options: PipeOptions) Io.File.Mode { if (!std.fs.has_executable_bit or options.mode_mode == .ignore) return default_mode; @@ -1139,16 +1147,17 @@ test fileMode { test "executable bit" { if (!std.fs.has_executable_bit) return error.SkipZigTest; + const io = testing.io; const S = std.posix.S; const data = @embedFile("tar/testdata/example.tar"); for ([_]PipeOptions.ModeMode{ .ignore, .executable_bit_only }) |opt| { - var reader: std.Io.Reader = .fixed(data); + var reader: Io.Reader = .fixed(data); var tmp = testing.tmpDir(.{ .follow_symlinks = false }); //defer tmp.cleanup(); - pipeToFileSystem(tmp.dir, &reader, .{ + pipeToFileSystem(io, tmp.dir, &reader, .{ .strip_components = 1, .exclude_empty_directories = true, .mode_mode = opt, diff --git a/lib/std/tar/test.zig b/lib/std/tar/test.zig index 780e2b844c..b3fbd8f4b3 100644 --- a/lib/std/tar/test.zig +++ b/lib/std/tar/test.zig @@ -424,6 +424,7 @@ test "insufficient buffer in Header name filed" { } test "should not overwrite existing file" { + const io = testing.io; // Starting from this folder structure: // $ tree root // root @@ -469,17 +470,18 @@ test "should not overwrite existing file" { defer root.cleanup(); try testing.expectError( error.PathAlreadyExists, - tar.pipeToFileSystem(root.dir, &r, .{ .mode_mode = .ignore, .strip_components = 1 }), + tar.pipeToFileSystem(io, root.dir, &r, .{ .mode_mode = .ignore, .strip_components = 1 }), ); // Unpack with strip_components = 0 should pass r = .fixed(data); var root2 = std.testing.tmpDir(.{}); defer root2.cleanup(); - try tar.pipeToFileSystem(root2.dir, &r, .{ .mode_mode = .ignore, .strip_components = 0 }); + try tar.pipeToFileSystem(io, root2.dir, &r, .{ .mode_mode = .ignore, .strip_components = 0 }); } test "case sensitivity" { + const io = testing.io; // Mimicking issue #18089, this tar contains, same file name in two case // sensitive name version. Should fail on case insensitive file systems. // @@ -495,7 +497,7 @@ test "case sensitivity" { var root = std.testing.tmpDir(.{}); defer root.cleanup(); - tar.pipeToFileSystem(root.dir, &r, .{ .mode_mode = .ignore, .strip_components = 1 }) catch |err| { + tar.pipeToFileSystem(io, root.dir, &r, .{ .mode_mode = .ignore, .strip_components = 1 }) catch |err| { // on case insensitive fs we fail on overwrite existing file try testing.expectEqual(error.PathAlreadyExists, err); return; diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 186cafad59..63131d771c 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -613,9 +613,9 @@ pub const TmpDir = struct { const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count); pub fn cleanup(self: *TmpDir) void { - self.dir.close(); + self.dir.close(io); self.parent_dir.deleteTree(&self.sub_path) catch {}; - self.parent_dir.close(); + self.parent_dir.close(io); self.* = undefined; } }; @@ -629,7 +629,7 @@ pub fn tmpDir(opts: std.fs.Dir.OpenOptions) TmpDir { const cwd = std.fs.cwd(); var cache_dir = cwd.makeOpenPath(".zig-cache", .{}) catch @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir"); - defer cache_dir.close(); + defer cache_dir.close(io); const parent_dir = cache_dir.makeOpenPath("tmp", .{}) catch @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir"); const dir = parent_dir.makeOpenPath(&sub_path, opts) catch diff --git a/lib/std/zig/LibCInstallation.zig b/lib/std/zig/LibCInstallation.zig index 2ab4e48570..c8bde2ab02 100644 --- a/lib/std/zig/LibCInstallation.zig +++ b/lib/std/zig/LibCInstallation.zig @@ -1,4 +1,18 @@ //! See the render function implementation for documentation of the fields. +const LibCInstallation = @This(); + +const builtin = @import("builtin"); +const is_darwin = builtin.target.os.tag.isDarwin(); +const is_windows = builtin.target.os.tag == .windows; +const is_haiku = builtin.target.os.tag == .haiku; + +const std = @import("std"); +const Io = std.Io; +const Target = std.Target; +const fs = std.fs; +const Allocator = std.mem.Allocator; +const Path = std.Build.Cache.Path; +const log = std.log.scoped(.libc_installation); include_dir: ?[]const u8 = null, sys_include_dir: ?[]const u8 = null, @@ -157,6 +171,7 @@ pub fn render(self: LibCInstallation, out: *std.Io.Writer) !void { pub const FindNativeOptions = struct { allocator: Allocator, + io: Io, target: *const std.Target, /// If enabled, will print human-friendly errors to stderr. @@ -165,29 +180,32 @@ pub const FindNativeOptions = struct { /// Finds the default, native libc. pub fn findNative(args: FindNativeOptions) FindError!LibCInstallation { + const gpa = args.allocator; + const io = args.io; + var self: LibCInstallation = .{}; if (is_darwin and args.target.os.tag.isDarwin()) { - if (!std.zig.system.darwin.isSdkInstalled(args.allocator)) + if (!std.zig.system.darwin.isSdkInstalled(gpa)) return error.DarwinSdkNotFound; - const sdk = std.zig.system.darwin.getSdk(args.allocator, args.target) orelse + const sdk = std.zig.system.darwin.getSdk(gpa, args.target) orelse return error.DarwinSdkNotFound; - defer args.allocator.free(sdk); + defer gpa.free(sdk); - self.include_dir = try fs.path.join(args.allocator, &.{ + self.include_dir = try fs.path.join(gpa, &.{ sdk, "usr/include", }); - self.sys_include_dir = try fs.path.join(args.allocator, &.{ + self.sys_include_dir = try fs.path.join(gpa, &.{ sdk, "usr/include", }); return self; } else if (is_windows) { - const sdk = std.zig.WindowsSdk.find(args.allocator, args.target.cpu.arch) catch |err| switch (err) { + const sdk = std.zig.WindowsSdk.find(gpa, io, args.target.cpu.arch) catch |err| switch (err) { error.NotFound => return error.WindowsSdkNotFound, error.PathTooLong => return error.WindowsSdkNotFound, error.OutOfMemory => return error.OutOfMemory, }; - defer sdk.free(args.allocator); + defer sdk.free(gpa); try self.findNativeMsvcIncludeDir(args, sdk); try self.findNativeMsvcLibDir(args, sdk); @@ -197,16 +215,16 @@ pub fn findNative(args: FindNativeOptions) FindError!LibCInstallation { } else if (is_haiku) { try self.findNativeIncludeDirPosix(args); try self.findNativeGccDirHaiku(args); - self.crt_dir = try args.allocator.dupeZ(u8, "/system/develop/lib"); + self.crt_dir = try gpa.dupeZ(u8, "/system/develop/lib"); } else if (builtin.target.os.tag == .illumos) { // There is only one libc, and its headers/libraries are always in the same spot. - self.include_dir = try args.allocator.dupeZ(u8, "/usr/include"); - self.sys_include_dir = try args.allocator.dupeZ(u8, "/usr/include"); - self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib/64"); + self.include_dir = try gpa.dupeZ(u8, "/usr/include"); + self.sys_include_dir = try gpa.dupeZ(u8, "/usr/include"); + self.crt_dir = try gpa.dupeZ(u8, "/usr/lib/64"); } else if (std.process.can_spawn) { try self.findNativeIncludeDirPosix(args); switch (builtin.target.os.tag) { - .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib"), + .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try gpa.dupeZ(u8, "/usr/lib"), .linux => try self.findNativeCrtDirPosix(args), else => {}, } @@ -229,6 +247,7 @@ pub fn deinit(self: *LibCInstallation, allocator: Allocator) void { fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void { const allocator = args.allocator; + const io = args.io; // Detect infinite loops. var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) { @@ -326,7 +345,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) F else => return error.FileSystem, }; - defer search_dir.close(); + defer search_dir.close(io); if (self.include_dir == null) { if (search_dir.access(include_dir_example_file, .{})) |_| { @@ -361,6 +380,7 @@ fn findNativeIncludeDirWindows( sdk: std.zig.WindowsSdk, ) FindError!void { const allocator = args.allocator; + const io = args.io; var install_buf: [2]std.zig.WindowsSdk.Installation = undefined; const installs = fillInstallations(&install_buf, sdk); @@ -380,7 +400,7 @@ fn findNativeIncludeDirWindows( else => return error.FileSystem, }; - defer dir.close(); + defer dir.close(io); dir.access("stdlib.h", .{}) catch |err| switch (err) { error.FileNotFound => continue, @@ -400,6 +420,7 @@ fn findNativeCrtDirWindows( sdk: std.zig.WindowsSdk, ) FindError!void { const allocator = args.allocator; + const io = args.io; var install_buf: [2]std.zig.WindowsSdk.Installation = undefined; const installs = fillInstallations(&install_buf, sdk); @@ -427,7 +448,7 @@ fn findNativeCrtDirWindows( else => return error.FileSystem, }; - defer dir.close(); + defer dir.close(io); dir.access("ucrt.lib", .{}) catch |err| switch (err) { error.FileNotFound => continue, @@ -467,6 +488,7 @@ fn findNativeKernel32LibDir( sdk: std.zig.WindowsSdk, ) FindError!void { const allocator = args.allocator; + const io = args.io; var install_buf: [2]std.zig.WindowsSdk.Installation = undefined; const installs = fillInstallations(&install_buf, sdk); @@ -494,7 +516,7 @@ fn findNativeKernel32LibDir( else => return error.FileSystem, }; - defer dir.close(); + defer dir.close(io); dir.access("kernel32.lib", .{}) catch |err| switch (err) { error.FileNotFound => continue, @@ -513,6 +535,7 @@ fn findNativeMsvcIncludeDir( sdk: std.zig.WindowsSdk, ) FindError!void { const allocator = args.allocator; + const io = args.io; const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCStdLibHeaderNotFound; const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound; @@ -529,7 +552,7 @@ fn findNativeMsvcIncludeDir( else => return error.FileSystem, }; - defer dir.close(); + defer dir.close(io); dir.access("vcruntime.h", .{}) catch |err| switch (err) { error.FileNotFound => return error.LibCStdLibHeaderNotFound, @@ -1015,17 +1038,3 @@ pub fn resolveCrtPaths( }, } } - -const LibCInstallation = @This(); -const std = @import("std"); -const builtin = @import("builtin"); -const Target = std.Target; -const fs = std.fs; -const Allocator = std.mem.Allocator; -const Path = std.Build.Cache.Path; - -const is_darwin = builtin.target.os.tag.isDarwin(); -const is_windows = builtin.target.os.tag == .windows; -const is_haiku = builtin.target.os.tag == .haiku; - -const log = std.log.scoped(.libc_installation); diff --git a/lib/std/zig/WindowsSdk.zig b/lib/std/zig/WindowsSdk.zig index 89d608633c..a7b16e1bed 100644 --- a/lib/std/zig/WindowsSdk.zig +++ b/lib/std/zig/WindowsSdk.zig @@ -1,7 +1,10 @@ const WindowsSdk = @This(); const builtin = @import("builtin"); + const std = @import("std"); +const Io = std.Io; const Writer = std.Io.Writer; +const Allocator = std.mem.Allocator; windows10sdk: ?Installation, windows81sdk: ?Installation, @@ -20,7 +23,7 @@ const product_version_max_length = version_major_minor_max_length + ".65535".len /// Find path and version of Windows 10 SDK and Windows 8.1 SDK, and find path to MSVC's `lib/` directory. /// Caller owns the result's fields. /// After finishing work, call `free(allocator)`. -pub fn find(allocator: std.mem.Allocator, arch: std.Target.Cpu.Arch) error{ OutOfMemory, NotFound, PathTooLong }!WindowsSdk { +pub fn find(allocator: Allocator, arch: std.Target.Cpu.Arch) error{ OutOfMemory, NotFound, PathTooLong }!WindowsSdk { if (builtin.os.tag != .windows) return error.NotFound; //note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed @@ -58,7 +61,7 @@ pub fn find(allocator: std.mem.Allocator, arch: std.Target.Cpu.Arch) error{ OutO }; } -pub fn free(sdk: WindowsSdk, allocator: std.mem.Allocator) void { +pub fn free(sdk: WindowsSdk, allocator: Allocator) void { if (sdk.windows10sdk) |*w10sdk| { w10sdk.free(allocator); } @@ -75,7 +78,7 @@ pub fn free(sdk: WindowsSdk, allocator: std.mem.Allocator) void { /// Caller owns result. fn iterateAndFilterByVersion( iterator: *std.fs.Dir.Iterator, - allocator: std.mem.Allocator, + allocator: Allocator, prefix: []const u8, ) error{OutOfMemory}![][]const u8 { const Version = struct { @@ -174,7 +177,7 @@ const RegistryWtf8 = struct { /// Get string from registry. /// Caller owns result. - pub fn getString(reg: RegistryWtf8, allocator: std.mem.Allocator, subkey: []const u8, value_name: []const u8) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }![]u8 { + pub fn getString(reg: RegistryWtf8, allocator: Allocator, subkey: []const u8, value_name: []const u8) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }![]u8 { const subkey_wtf16le: [:0]const u16 = subkey_wtf16le: { var subkey_wtf16le_buf: [RegistryWtf16Le.key_name_max_len]u16 = undefined; const subkey_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(subkey_wtf16le_buf[0..], subkey) catch unreachable; @@ -282,7 +285,7 @@ const RegistryWtf16Le = struct { } /// Get string ([:0]const u16) from registry. - fn getString(reg: RegistryWtf16Le, allocator: std.mem.Allocator, subkey_wtf16le: [:0]const u16, value_name_wtf16le: [:0]const u16) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }![]const u16 { + fn getString(reg: RegistryWtf16Le, allocator: Allocator, subkey_wtf16le: [:0]const u16, value_name_wtf16le: [:0]const u16) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }![]const u16 { var actual_type: windows.ULONG = undefined; // Calculating length to allocate @@ -416,7 +419,7 @@ pub const Installation = struct { /// Caller owns the result's fields. /// After finishing work, call `free(allocator)`. fn find( - allocator: std.mem.Allocator, + allocator: Allocator, roots_key: RegistryWtf8, roots_subkey: []const u8, prefix: []const u8, @@ -437,7 +440,8 @@ pub const Installation = struct { } fn findFromRoot( - allocator: std.mem.Allocator, + allocator: Allocator, + io: Io, roots_key: RegistryWtf8, roots_subkey: []const u8, prefix: []const u8, @@ -478,7 +482,7 @@ pub const Installation = struct { error.NameTooLong => return error.PathTooLong, else => return error.InstallationNotFound, }; - defer sdk_lib_dir.close(); + defer sdk_lib_dir.close(io); var iterator = sdk_lib_dir.iterate(); const versions = try iterateAndFilterByVersion(&iterator, allocator, prefix); @@ -495,7 +499,7 @@ pub const Installation = struct { } fn findFromInstallationFolder( - allocator: std.mem.Allocator, + allocator: Allocator, version_key_name: []const u8, ) error{ OutOfMemory, InstallationNotFound, PathTooLong, VersionTooLong }!Installation { var key_name_buf: [RegistryWtf16Le.key_name_max_len]u8 = undefined; @@ -597,14 +601,14 @@ pub const Installation = struct { return (reg_value == 1); } - fn free(install: Installation, allocator: std.mem.Allocator) void { + fn free(install: Installation, allocator: Allocator) void { allocator.free(install.path); allocator.free(install.version); } }; const MsvcLibDir = struct { - fn findInstancesDirViaSetup(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { + fn findInstancesDirViaSetup(allocator: Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { const vs_setup_key_path = "SOFTWARE\\Microsoft\\VisualStudio\\Setup"; const vs_setup_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, vs_setup_key_path, .{}) catch |err| switch (err) { error.KeyNotFound => return error.PathNotFound, @@ -629,7 +633,7 @@ const MsvcLibDir = struct { return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return error.PathNotFound; } - fn findInstancesDirViaCLSID(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { + fn findInstancesDirViaCLSID(allocator: Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { const setup_configuration_clsid = "{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}"; const setup_config_key = RegistryWtf8.openKey(windows.HKEY_CLASSES_ROOT, "CLSID\\" ++ setup_configuration_clsid, .{}) catch |err| switch (err) { error.KeyNotFound => return error.PathNotFound, @@ -665,7 +669,7 @@ const MsvcLibDir = struct { return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return error.PathNotFound; } - fn findInstancesDir(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { + fn findInstancesDir(allocator: Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { // First, try getting the packages cache path from the registry. // This only seems to exist when the path is different from the default. method1: { @@ -748,13 +752,13 @@ const MsvcLibDir = struct { /// /// The logic in this function is intended to match what ISetupConfiguration does /// under-the-hood, as verified using Procmon. - fn findViaCOM(allocator: std.mem.Allocator, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { + fn findViaCOM(allocator: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { // Typically `%PROGRAMDATA%\Microsoft\VisualStudio\Packages\_Instances` // This will contain directories with names of instance IDs like 80a758ca, // which will contain `state.json` files that have the version and // installation directory. var instances_dir = try findInstancesDir(allocator); - defer instances_dir.close(); + defer instances_dir.close(io); var state_subpath_buf: [std.fs.max_name_bytes + 32]u8 = undefined; var latest_version_lib_dir: std.ArrayList(u8) = .empty; @@ -791,7 +795,7 @@ const MsvcLibDir = struct { const installation_path = parsed.value.object.get("installationPath") orelse continue; if (installation_path != .string) continue; - const lib_dir_path = libDirFromInstallationPath(allocator, installation_path.string, arch) catch |err| switch (err) { + const lib_dir_path = libDirFromInstallationPath(allocator, io, installation_path.string, arch) catch |err| switch (err) { error.OutOfMemory => |e| return e, error.PathNotFound => continue, }; @@ -806,7 +810,12 @@ const MsvcLibDir = struct { return latest_version_lib_dir.toOwnedSlice(allocator); } - fn libDirFromInstallationPath(allocator: std.mem.Allocator, installation_path: []const u8, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { + fn libDirFromInstallationPath( + allocator: Allocator, + io: Io, + installation_path: []const u8, + arch: std.Target.Cpu.Arch, + ) error{ OutOfMemory, PathNotFound }![]const u8 { var lib_dir_buf = try std.array_list.Managed(u8).initCapacity(allocator, installation_path.len + 64); errdefer lib_dir_buf.deinit(); @@ -837,7 +846,7 @@ const MsvcLibDir = struct { else => unreachable, }); - if (!verifyLibDir(lib_dir_buf.items)) { + if (!verifyLibDir(io, lib_dir_buf.items)) { return error.PathNotFound; } @@ -845,7 +854,7 @@ const MsvcLibDir = struct { } // https://learn.microsoft.com/en-us/visualstudio/install/tools-for-managing-visual-studio-instances?view=vs-2022#editing-the-registry-for-a-visual-studio-instance - fn findViaRegistry(allocator: std.mem.Allocator, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { + fn findViaRegistry(allocator: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { // %localappdata%\Microsoft\VisualStudio\ // %appdata%\Local\Microsoft\VisualStudio\ @@ -859,7 +868,7 @@ const MsvcLibDir = struct { var visualstudio_folder = std.fs.openDirAbsolute(visualstudio_folder_path, .{ .iterate = true, }) catch return error.PathNotFound; - defer visualstudio_folder.close(); + defer visualstudio_folder.close(io); var iterator = visualstudio_folder.iterate(); break :vs_versions try iterateAndFilterByVersion(&iterator, allocator, ""); @@ -926,14 +935,14 @@ const MsvcLibDir = struct { }; errdefer allocator.free(msvc_dir); - if (!verifyLibDir(msvc_dir)) { + if (!verifyLibDir(io, msvc_dir)) { return error.PathNotFound; } return msvc_dir; } - fn findViaVs7Key(allocator: std.mem.Allocator, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { + fn findViaVs7Key(allocator: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { var base_path: std.array_list.Managed(u8) = base_path: { try_env: { var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) { @@ -989,7 +998,7 @@ const MsvcLibDir = struct { else => unreachable, }); - if (!verifyLibDir(base_path.items)) { + if (!verifyLibDir(io, base_path.items)) { return error.PathNotFound; } @@ -997,11 +1006,11 @@ const MsvcLibDir = struct { return full_path; } - fn verifyLibDir(lib_dir_path: []const u8) bool { + fn verifyLibDir(io: Io, lib_dir_path: []const u8) bool { std.debug.assert(std.fs.path.isAbsolute(lib_dir_path)); // should be already handled in `findVia*` var dir = std.fs.openDirAbsolute(lib_dir_path, .{}) catch return false; - defer dir.close(); + defer dir.close(io); const stat = dir.statFile("vcruntime.lib") catch return false; if (stat.kind != .file) @@ -1012,12 +1021,12 @@ const MsvcLibDir = struct { /// Find path to MSVC's `lib/` directory. /// Caller owns the result. - pub fn find(allocator: std.mem.Allocator, arch: std.Target.Cpu.Arch) error{ OutOfMemory, MsvcLibDirNotFound }![]const u8 { - const full_path = MsvcLibDir.findViaCOM(allocator, arch) catch |err1| switch (err1) { + pub fn find(allocator: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, MsvcLibDirNotFound }![]const u8 { + const full_path = MsvcLibDir.findViaCOM(allocator, io, arch) catch |err1| switch (err1) { error.OutOfMemory => return error.OutOfMemory, - error.PathNotFound => MsvcLibDir.findViaRegistry(allocator, arch) catch |err2| switch (err2) { + error.PathNotFound => MsvcLibDir.findViaRegistry(allocator, io, arch) catch |err2| switch (err2) { error.OutOfMemory => return error.OutOfMemory, - error.PathNotFound => MsvcLibDir.findViaVs7Key(allocator, arch) catch |err3| switch (err3) { + error.PathNotFound => MsvcLibDir.findViaVs7Key(allocator, io, arch) catch |err3| switch (err3) { error.OutOfMemory => return error.OutOfMemory, error.PathNotFound => return error.MsvcLibDirNotFound, }, diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index 587ac82c70..9a52ae2c81 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -1,14 +1,17 @@ +const builtin = @import("builtin"); +const Builder = @This(); + const std = @import("../../std.zig"); +const Io = std.Io; const Allocator = std.mem.Allocator; const assert = std.debug.assert; -const bitcode_writer = @import("bitcode_writer.zig"); -const Builder = @This(); -const builtin = @import("builtin"); const DW = std.dwarf; -const ir = @import("ir.zig"); const log = std.log.scoped(.llvm); const Writer = std.Io.Writer; +const bitcode_writer = @import("bitcode_writer.zig"); +const ir = @import("ir.zig"); + gpa: Allocator, strip: bool, @@ -9579,11 +9582,11 @@ pub fn dump(b: *Builder) void { b.printToFile(stderr, &buffer) catch {}; } -pub fn printToFilePath(b: *Builder, dir: std.fs.Dir, path: []const u8) !void { +pub fn printToFilePath(b: *Builder, io: Io, dir: std.fs.Dir, path: []const u8) !void { var buffer: [4000]u8 = undefined; - const file = try dir.createFile(path, .{}); - defer file.close(); - try b.printToFile(file, &buffer); + const file = try dir.createFile(io, path, .{}); + defer file.close(io); + try b.printToFile(io, file, &buffer); } pub fn printToFile(b: *Builder, file: std.fs.File, buffer: []u8) !void { diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 290cd8d388..4f0c11797d 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -847,7 +847,7 @@ fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion { error.Unexpected => |e| return e, error.Canceled => |e| return e, }; - defer file.close(); + defer file.close(io); // Empirically, glibc 2.34 libc.so .dynstr section is 32441 bytes on my system. var buffer: [8000]u8 = undefined; @@ -1051,7 +1051,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ else => |e| return e, }; var is_elf_file = false; - defer if (!is_elf_file) file.close(); + defer if (!is_elf_file) file.close(io); file_reader = .initAdapted(file, io, &file_reader_buffer); file_name = undefined; // it aliases file_reader_buffer diff --git a/lib/std/zig/system/linux.zig b/lib/std/zig/system/linux.zig index 6b4f0cf6f9..668e5a1d99 100644 --- a/lib/std/zig/system/linux.zig +++ b/lib/std/zig/system/linux.zig @@ -447,7 +447,7 @@ pub fn detectNativeCpuAndFeatures(io: Io) ?Target.Cpu { var file = fs.openFileAbsolute("/proc/cpuinfo", .{}) catch |err| switch (err) { else => return null, }; - defer file.close(); + defer file.close(io); var buffer: [4096]u8 = undefined; // "flags" lines can get pretty long. var file_reader = file.reader(io, &buffer); diff --git a/lib/std/zip.zig b/lib/std/zip.zig index 583377e00a..c2dbaf5b81 100644 --- a/lib/std/zip.zig +++ b/lib/std/zip.zig @@ -554,17 +554,19 @@ pub const Iterator = struct { return; } + const io = stream.io; + const out_file = blk: { if (std.fs.path.dirname(filename)) |dirname| { var parent_dir = try dest.makeOpenPath(dirname, .{}); - defer parent_dir.close(); + defer parent_dir.close(io); const basename = std.fs.path.basename(filename); break :blk try parent_dir.createFile(basename, .{ .exclusive = true }); } break :blk try dest.createFile(filename, .{ .exclusive = true }); }; - defer out_file.close(); + defer out_file.close(io); var out_file_buffer: [1024]u8 = undefined; var file_writer = out_file.writer(&out_file_buffer); const local_data_file_offset: u64 = |
