aboutsummaryrefslogtreecommitdiff
path: root/lib/std/fs/AtomicFile.zig
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std/fs/AtomicFile.zig')
-rw-r--r--lib/std/fs/AtomicFile.zig98
1 files changed, 52 insertions, 46 deletions
diff --git a/lib/std/fs/AtomicFile.zig b/lib/std/fs/AtomicFile.zig
index 17a17f8993..96793aec72 100644
--- a/lib/std/fs/AtomicFile.zig
+++ b/lib/std/fs/AtomicFile.zig
@@ -1,6 +1,13 @@
-file: File,
-// TODO either replace this with rand_buf or use []u16 on Windows
-tmp_path_buf: [tmp_path_len:0]u8,
+const AtomicFile = @This();
+const std = @import("../std.zig");
+const File = std.fs.File;
+const Dir = std.fs.Dir;
+const fs = std.fs;
+const assert = std.debug.assert;
+const posix = std.posix;
+
+file_writer: File.Writer,
+random_integer: u64,
dest_basename: []const u8,
file_open: bool,
file_exists: bool,
@@ -9,35 +16,24 @@ dir: Dir,
pub const InitError = File.OpenError;
-pub const random_bytes_len = 12;
-const tmp_path_len = fs.base64_encoder.calcSize(random_bytes_len);
-
/// Note that the `Dir.atomicFile` API may be more handy than this lower-level function.
pub fn init(
dest_basename: []const u8,
mode: File.Mode,
dir: Dir,
close_dir_on_deinit: bool,
+ write_buffer: []u8,
) InitError!AtomicFile {
- var rand_buf: [random_bytes_len]u8 = undefined;
- var tmp_path_buf: [tmp_path_len:0]u8 = undefined;
-
while (true) {
- std.crypto.random.bytes(rand_buf[0..]);
- const tmp_path = fs.base64_encoder.encode(&tmp_path_buf, &rand_buf);
- tmp_path_buf[tmp_path.len] = 0;
-
- const file = dir.createFile(
- tmp_path,
- .{ .mode = mode, .exclusive = true },
- ) catch |err| switch (err) {
+ const random_integer = std.crypto.random.int(u64);
+ const tmp_sub_path = std.fmt.hex(random_integer);
+ const file = dir.createFile(&tmp_sub_path, .{ .mode = mode, .exclusive = true }) catch |err| switch (err) {
error.PathAlreadyExists => continue,
else => |e| return e,
};
-
- return AtomicFile{
- .file = file,
- .tmp_path_buf = tmp_path_buf,
+ return .{
+ .file_writer = file.writer(write_buffer),
+ .random_integer = random_integer,
.dest_basename = dest_basename,
.file_open = true,
.file_exists = true,
@@ -48,41 +44,51 @@ pub fn init(
}
/// Always call deinit, even after a successful finish().
-pub fn deinit(self: *AtomicFile) void {
- if (self.file_open) {
- self.file.close();
- self.file_open = false;
+pub fn deinit(af: *AtomicFile) void {
+ if (af.file_open) {
+ af.file_writer.file.close();
+ af.file_open = false;
}
- if (self.file_exists) {
- self.dir.deleteFile(&self.tmp_path_buf) catch {};
- self.file_exists = false;
+ if (af.file_exists) {
+ const tmp_sub_path = std.fmt.hex(af.random_integer);
+ af.dir.deleteFile(&tmp_sub_path) catch {};
+ af.file_exists = false;
}
- if (self.close_dir_on_deinit) {
- self.dir.close();
+ if (af.close_dir_on_deinit) {
+ af.dir.close();
}
- self.* = undefined;
+ af.* = undefined;
}
-pub const FinishError = posix.RenameError;
+pub const FlushError = File.WriteError;
+
+pub fn flush(af: *AtomicFile) FlushError!void {
+ af.file_writer.interface.flush() catch |err| switch (err) {
+ error.WriteFailed => return af.file_writer.err.?,
+ };
+}
+
+pub const RenameIntoPlaceError = posix.RenameError;
/// On Windows, this function introduces a period of time where some file
/// system operations on the destination file will result in
/// `error.AccessDenied`, including rename operations (such as the one used in
/// this function).
-pub fn finish(self: *AtomicFile) FinishError!void {
- assert(self.file_exists);
- if (self.file_open) {
- self.file.close();
- self.file_open = false;
+pub fn renameIntoPlace(af: *AtomicFile) RenameIntoPlaceError!void {
+ assert(af.file_exists);
+ if (af.file_open) {
+ af.file_writer.file.close();
+ af.file_open = false;
}
- try posix.renameat(self.dir.fd, self.tmp_path_buf[0..], self.dir.fd, self.dest_basename);
- self.file_exists = false;
+ const tmp_sub_path = std.fmt.hex(af.random_integer);
+ try posix.renameat(af.dir.fd, &tmp_sub_path, af.dir.fd, af.dest_basename);
+ af.file_exists = false;
}
-const AtomicFile = @This();
-const std = @import("../std.zig");
-const File = std.fs.File;
-const Dir = std.fs.Dir;
-const fs = std.fs;
-const assert = std.debug.assert;
-const posix = std.posix;
+pub const FinishError = FlushError || RenameIntoPlaceError;
+
+/// Combination of `flush` followed by `renameIntoPlace`.
+pub fn finish(af: *AtomicFile) FinishError!void {
+ try af.flush();
+ try af.renameIntoPlace();
+}