From 95fdff3feb8bbdfb33a8e0f6cd96b4457984e2f4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 14 Jul 2019 22:29:39 -0400 Subject: [PATCH] std.fs.updateFile: make path if necessary --- std/fs.zig | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/std/fs.zig b/std/fs.zig index df55fb36b2..c656c34533 100644 --- a/std/fs.zig +++ b/std/fs.zig @@ -80,11 +80,12 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus { return updateFileMode(source_path, dest_path, null); } -/// Check the file size and mtime of `source_path` and `dest_path`. If they are equal, does nothing. -/// Otherwise, atomically copies `source_path` to `dest_path`. The destination file gains the mtime -/// and atime of the source file so that the next call to `updateFile` will not need a copy. -/// TODO https://github.com/ziglang/zig/issues/2885 +/// Check the file size, mtime, and mode of `source_path` and `dest_path`. If they are equal, does nothing. +/// Otherwise, atomically copies `source_path` to `dest_path`. The destination file gains the mtime, +/// atime, and mode of the source file so that the next call to `updateFile` will not need a copy. /// Returns the previous status of the file before updating. +/// If any of the directories do not exist for dest_path, they are created. +/// TODO https://github.com/ziglang/zig/issues/2885 pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus { var src_file = try File.openRead(source_path); defer src_file.close(); @@ -108,9 +109,32 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil return PrevStatus.fresh; } } + const actual_mode = mode orelse src_stat.mode; const in_stream = &src_file.inStream().stream; - var atomic_file = try AtomicFile.init(dest_path, mode orelse src_stat.mode); + // TODO this logic could be made more efficient by calling makePath, once + // that API does not require an allocator + var atomic_file = make_atomic_file: while (true) { + const af = AtomicFile.init(dest_path, actual_mode) catch |err| switch (err) { + error.FileNotFound => { + var p = dest_path; + while (path.dirname(p)) |dirname| { + makeDir(dirname) catch |e| switch (e) { + error.FileNotFound => { + p = dirname; + continue; + }, + else => return e, + }; + continue :make_atomic_file; + } else { + return err; + } + }, + else => |e| return e, + }; + break af; + } else unreachable; defer atomic_file.deinit(); var buf: [mem.page_size * 6]u8 = undefined;