zig/lib/std/Io/File/Atomic.zig
Andrew Kelley 1ddae8585a std.Io.Threaded: add procfs fallback for fileHardLink
oof what a clunky set of functionality we have here
2026-01-07 11:03:37 -08:00

86 lines
2.6 KiB
Zig

const Atomic = @This();
const std = @import("../../std.zig");
const Io = std.Io;
const File = std.Io.File;
const Dir = std.Io.Dir;
const assert = std.debug.assert;
file: File,
file_basename_hex: u64,
file_open: bool,
file_exists: bool,
dir: Dir,
close_dir_on_deinit: bool,
dest_sub_path: []const u8,
pub const InitError = File.OpenError;
/// To release all resources, always call `deinit`, even after a successful
/// `finish`.
pub fn deinit(af: *Atomic, io: Io) void {
if (af.file_open) {
af.file.close(io);
af.file_open = false;
}
if (af.file_exists) {
const tmp_sub_path = std.fmt.hex(af.file_basename_hex);
af.dir.deleteFile(io, &tmp_sub_path) catch {};
af.file_exists = false;
}
if (af.close_dir_on_deinit) {
af.dir.close(io);
af.close_dir_on_deinit = false;
}
af.* = undefined;
}
pub const LinkError = File.HardLinkError || Dir.RenamePreserveError;
/// Atomically materializes the file into place, failing with
/// `error.PathAlreadyExists` if something already exists there.
///
/// If this operation could not be done with an unnamed temporary file, the
/// named temporary file will be deleted in a following operation, which may
/// independently fail. The result of that operation is stored in `delete_err`.
pub fn link(af: *Atomic, io: Io) LinkError!void {
if (af.file_exists) {
if (af.file_open) {
af.file.close(io);
af.file_open = false;
}
const tmp_sub_path = std.fmt.hex(af.file_basename_hex);
try af.dir.renamePreserve(&tmp_sub_path, af.dir, af.dest_sub_path, io);
af.file_exists = false;
} else {
assert(af.file_open);
try af.file.hardLink(io, af.dir, af.dest_sub_path, .{});
af.file.close(io);
af.file_open = false;
}
}
pub const ReplaceError = Dir.RenameError;
/// Atomically materializes the file into place, replacing any file that
/// already exists there.
///
/// Calling this function requires setting `CreateFileAtomicOptions.replace` to
/// `true`.
///
/// 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 replace(af: *Atomic, io: Io) ReplaceError!void {
assert(af.file_exists); // Wrong value for `CreateFileAtomicOptions.replace`.
if (af.file_open) {
af.file.close(io);
af.file_open = false;
}
const tmp_sub_path = std.fmt.hex(af.file_basename_hex);
try af.dir.rename(&tmp_sub_path, af.dir, af.dest_sub_path, io);
af.file_exists = false;
}