Skip to content

Instantly share code, notes, and snippets.

@congyiwu
Created July 8, 2020 02:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save congyiwu/59a4f6777d8a05a3e41c5ce236168406 to your computer and use it in GitHub Desktop.
Save congyiwu/59a4f6777d8a05a3e41c5ce236168406 to your computer and use it in GitHub Desktop.
AtomicWriteFile.cs
/// <summary>
/// Atomically create or replace file at <paramref name="path"/>, with contents provided by
/// <paramref name="write"/>.
/// </summary>
/// <remarks>
/// Although <paramref name="path"/> is atomically updated, the contents of <c>tmpPath</c> are
/// undefined if this method does not return normally.
/// </remarks>
public static void AtomicWriteFile(string path, Action<Stream> write)
{
// File.Replace() requires tmpPath to be in the same volume as path
string tmpPath = path + "." + Path.GetRandomFileName();
using (var tmpStream = new FileStream(tmpPath, FileMode.CreateNew, FileAccess.Write))
{
write(tmpStream);
// Calls https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-flushfilebuffers
// to ensure that the content of `tmpPath` is persisted to the physical disk before the
// `File.Replace()` below.
//
// This is needed b/c NTFS journals metadata, but not file contents, and prevents a gap
// where power loss would result in corrupted contents at `path`.
tmpStream.Flush(flushToDisk: true);
}
// Calls https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-replacefilew
// to atomically replace `path` with `tmpPath`, while preserving attributes and ACLs of `path`.
File.Replace(sourceFileName: tmpPath, destinationFileName: path, destinationBackupFileName: null);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment