From 82a4aef1fa5d52631ee0d56ec9e421dc20a45afb Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 16 Apr 2022 11:07:27 -0700 Subject: [PATCH] TempDir implementation --- src/Command.zig | 10 +++--- src/TempDir.zig | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ src/main.zig | 1 + 3 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 src/TempDir.zig diff --git a/src/Command.zig b/src/Command.zig index 93001fc33..7c3ae6c63 100644 --- a/src/Command.zig +++ b/src/Command.zig @@ -17,6 +17,7 @@ const Command = @This(); const std = @import("std"); const builtin = @import("builtin"); +const TempDir = @import("TempDir.zig"); const os = std.os; const debug = std.debug; const testing = std.testing; @@ -161,12 +162,9 @@ test "Command: pre exec" { } test "Command: redirect stdout to file" { - const cwd = std.fs.cwd(); - var stdout = try cwd.createFile("test1234.txt", .{ - .read = true, - .truncate = true, - }); - defer cwd.deleteFile("test1234.txt") catch unreachable; + const td = try TempDir.create(); + defer td.deinit(); + var stdout = try td.dir.createFile("stdout.txt", .{ .read = true }); defer stdout.close(); var cmd: Command = .{ diff --git a/src/TempDir.zig b/src/TempDir.zig new file mode 100644 index 000000000..bdb73bc95 --- /dev/null +++ b/src/TempDir.zig @@ -0,0 +1,81 @@ +//! Creates a temporary directory with a random name. +const TempDir = @This(); + +const std = @import("std"); +const testing = std.testing; +const Dir = std.fs.Dir; + +const log = std.log.scoped(.tempdir); + +/// Dir is the directory handle +dir: Dir, + +/// Parent directory +parent: Dir, + +/// Name buffer that name points into. Generally do not use. To get the +/// name call the name() function. +name_buf: [TMP_PATH_LEN:0]u8, + +pub fn create() !TempDir { + var rand_buf: [RANDOM_BYTES]u8 = undefined; + var tmp_path_buf: [TMP_PATH_LEN:0]u8 = undefined; + + // TODO: use the real temp dir not cwd + const dir = std.fs.cwd(); + + // We now loop forever until we can find a directory that we can create. + while (true) { + std.crypto.random.bytes(rand_buf[0..]); + const tmp_path = b64_encoder.encode(&tmp_path_buf, &rand_buf); + tmp_path_buf[tmp_path.len] = 0; + + dir.makeDir(tmp_path) catch |err| switch (err) { + error.PathAlreadyExists => continue, + else => |e| return e, + }; + + return TempDir{ + .dir = try dir.openDir(tmp_path, .{}), + .parent = dir, + .name_buf = tmp_path_buf, + }; + } +} + +/// Name returns the name of the directory. This is just the basename +/// and is not the full absolute path. +pub fn name(self: TempDir) []const u8 { + return std.mem.sliceTo(&self.name_buf, 0); +} + +/// Finish with the temporary directory. This deletes all contents in the +/// directory. +pub fn deinit(self: TempDir) void { + self.parent.deleteTree(self.name()) catch |err| + log.err("error deleting temp dir err={}", .{err}); +} + +// The amount of random bytes to get to determine our filename. +const RANDOM_BYTES = 16; +const TMP_PATH_LEN = b64_encoder.calcSize(RANDOM_BYTES); + +// Base64 encoder, replacing the standard `+/` with `-_` so that it can +// be used in a file name on any filesystem. +const b64_encoder = std.base64.Base64Encoder.init(b64_alphabet, null); +const b64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*; + +test { + var td = try create(); + defer td.deinit(); + + const nameval = td.name(); + try testing.expect(nameval.len > 0); + + // Can open a new handle to it proves it exists. + _ = try td.parent.openDir(nameval, .{}); + + // Should be deleted after we deinit + td.deinit(); + try testing.expectError(error.FileNotFound, td.parent.openDir(nameval, .{})); +} diff --git a/src/main.zig b/src/main.zig index c4edf99b0..e3874e92d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -24,4 +24,5 @@ test { _ = @import("Grid.zig"); _ = @import("Pty.zig"); _ = @import("Command.zig"); + _ = @import("TempDir.zig"); }