Skip to content

Commit

Permalink
Remove async API calls and increment version to 0.4.0 (#52)
Browse files Browse the repository at this point in the history
For #50 .
  • Loading branch information
erikmav authored Oct 14, 2024
1 parent d672703 commit 59ea2bc
Show file tree
Hide file tree
Showing 7 changed files with 11 additions and 85 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<PropertyGroup>

<!-- DOCSYNC: When changing version number update README.md -->
<Version>0.3.12.0</Version>
<Version>0.4.0</Version>
<AssemblyVersion>0.9.9999.0</AssemblyVersion>

<Company>Microsoft</Company>
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
*Note*: This library is being superseded by CoW support now built into the Windows 11 24H2 release, as well as [Windows Server 2025](https://learn.microsoft.com/en-us/windows-server/get-started/whats-new-windows-server-2025#block-cloning-support). Use of CoW is automatic for Dev Drive and ReFS volumes starting in these OS versions. See related notes in our [blog entry](https://devblogs.microsoft.com/engineering-at-microsoft/copy-on-write-performance-and-debugging/) and linked earlier articles. We will continue to accept bug fixes for this library, and updates for the related [`Microsoft.Build.CopyOnWrite`](https://github.com/microsoft/MSBuildSdks/tree/main/src/CopyOnWrite) and [`Microsoft.Build.Artifacts`](https://github.com/microsoft/MSBuildSdks/tree/main/src/Artifacts) SDKs to use newer versions.

The CopyOnWrite library provides a .NET layer on top of Windows OS-specific logic that provides copy-on-write linking for files (a.k.a. CoW, file cloning, or reflinking). CoW linking provides the ability to copy a file without actually copying the original file's bytes from one disk location to another. The filesystem is in charge of ensuring that if the original file is modified or deleted, the CoW linked files remain unmodified by lazily copying the original file's bytes into each link. Unlike symlinks or hardlinks, writes to CoW links do not write through to the original file, as the filesystem breaks the link and copies in a lazy fashion. This enables scenarios like file caches where a single copy of a file held in a content-addressable or other store is safely linked to many locations in a filesystem with low I/O overhead.

*NOTE: Only Windows functionality is implemented. On Linux and Mac using `File.Copy` is sufficient as it automatically uses CoW for [Linux](https://github.com/dotnet/runtime/pull/64264) (starting in .NET 7, and as long as a CoW compatible filesystem like `btrfs` is in use) and [Mac](https://github.com/dotnet/runtime/pull/79243) (.NET 8). A [similar PR](https://github.com/dotnet/runtime/pull/88695) for Windows did not make it into .NET, however there is [work underway](https://devblogs.microsoft.com/engineering-at-microsoft/copy-on-write-in-win32-api-early-access/) to integrate CoW into the Windows API in a possible future release.
Expand Down Expand Up @@ -43,6 +45,7 @@ File clones on Windows do not actually allocate space on-drive for the clone. Th

[![NuGet version (CopyOnWrite)](https://img.shields.io/nuget/v/CopyOnWrite?style=plastic)](https://www.nuget.org/packages/CopyOnWrite)

* 0.4.0 October 2024: Remove async versions of `CloneFile` as the implementation did not use overlapped I/O anyway. CoW support is releasing in the Server 2025 and Win11 24H2 wave, built into the `CopyFile` API suite and on by default for Dev Drive and ReFS, so overlapped I/O in this library will never be implemented. Resolves https://github.com/microsoft/CopyOnWrite/issues/50
* 0.3.12 October 2024: Add ERROR_DEV_NOT_EXIST handling on getting free disk space
* 0.3.11 September 2024: Add ERROR_DEV_NOT_EXIST handling on volume enumeration
* 0.3.10 September 2024: Add ERROR_NO_SUCH_DEVICE handling on volume enumeration
Expand Down Expand Up @@ -80,12 +83,13 @@ File clones on Windows do not actually allocate space on-drive for the clone. Th
* 0.1.0 July 2021: Windows ReFS support. Mac and Linux throw NotSupportedException.

## Related Works
* MSBuild SDK plugin to replace Copy task with one that supports CoW: https://github.com/microsoft/MSBuildSdks/tree/main/src/CopyOnWrite
* MSBuild SDK plugin to replace Copy task with one that supports CoW: [Microsoft.Build.CopyOnWrite](https://github.com/microsoft/MSBuildSdks/tree/main/src/CopyOnWrite)
* CoW now available in the [`Microsoft.Build.Artifacts`](https://github.com/microsoft/MSBuildSdks/tree/main/src/Artifacts) MSBuild SDK plugin to speed up file copies for artifact staging.
* CoW and Dev Drive series on Engineering@Microsoft blog:
* [Intro](https://devblogs.microsoft.com/engineering-at-microsoft/dev-drive-and-copy-on-write-for-developer-performance/)
* [Dev Drive released in Win11](https://devblogs.microsoft.com/engineering-at-microsoft/dev-drive-is-now-available/)
* [CoW-in-Win32 early access](https://devblogs.microsoft.com/engineering-at-microsoft/copy-on-write-in-win32-api-early-access/) - which when released may make this package unneeded at least for Windows.
* [CoW-in-Win32 early access](https://devblogs.microsoft.com/engineering-at-microsoft/copy-on-write-in-win32-api-early-access/) - which when released will make this package unneeded.
* [Real-world performance and debugging](https://devblogs.microsoft.com/engineering-at-microsoft/copy-on-write-performance-and-debugging/)
* Rust CoW: https://github.com/nicokoch/reflink

## Contributing
Expand Down
29 changes: 1 addition & 28 deletions lib/ICopyOnWriteFilesystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// Licensed under the MIT License.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.CopyOnWrite;

Expand Down Expand Up @@ -74,8 +72,7 @@ public interface ICopyOnWriteFilesystem
/// <summary>
/// Creates a copy-on-write link at <paramref name="destination"/> pointing
/// to <paramref name="source"/>, overwriting any existing file or link.
/// Implicitly uses <see cref="CloneFlags.None"/>. NOTE: You should use
/// <see cref="CloneFileAsync"/> where possible.
/// Implicitly uses <see cref="CloneFlags.None"/>.
/// </summary>
/// <param name="source">The original file to which to link.</param>
/// <param name="destination">
Expand Down Expand Up @@ -104,30 +101,6 @@ public interface ICopyOnWriteFilesystem
/// </exception>
void CloneFile(string source, string destination, CloneFlags cloneFlags);

/// <summary>
/// Creates a copy-on-write link at <paramref name="destination"/> pointing
/// to <paramref name="source"/>, overwriting any existing file or link.
/// </summary>
/// <param name="source">The original file to which to link.</param>
/// <param name="destination">
/// The path where the link will be created. This must not already exist as a directory,
/// and the parent directory must exist before this call.
/// </param>
/// <param name="cloneFlags">Flags to change behavior during creation of the CoW link.</param>
/// <param name="cancellationToken">A cancellation token for this operation.</param>
/// <exception cref="System.NotSupportedException">Copy-on-write links are not supported between source and destination.</exception>
/// <exception cref="MaxCloneFileLinksExceededException">
/// The link attempt failed because a filesystem limit on the number of clones per file was exceeded. See <see cref="MaxClonesPerFile"/>.
/// </exception>
#if NET6_0 || NETSTANDARD2_1
ValueTask
#elif NETSTANDARD2_0
Task
#else
#error Target Framework not supported
#endif
CloneFileAsync(string source, string destination, CloneFlags cloneFlags, CancellationToken cancellationToken);

/// <summary>
/// Clears and recreates internal cached information about the computer's filesystem.
/// For performance, the copy-on-write filesystem implementations cache filesystem layout
Expand Down
15 changes: 0 additions & 15 deletions lib/Linux/LinuxCopyOnWriteFilesystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// Licensed under the MIT License.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.CopyOnWrite.Linux;

Expand All @@ -30,19 +28,6 @@ public void CloneFile(string source, string destination, CloneFlags cloneFlags)
throw new NotImplementedException();
}

public
#if NET6_0 || NETSTANDARD2_1
ValueTask
#elif NETSTANDARD2_0
Task
#else
#error Target Framework not supported
#endif
CloneFileAsync(string source, string destination, CloneFlags cloneFlags, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}

public void ClearFilesystemCache()
{
}
Expand Down
13 changes: 0 additions & 13 deletions lib/Mac/MacCopyOnWriteFilesystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,6 @@ public void CloneFile(string source, string destination, CloneFlags cloneFlags)
throw new NotImplementedException();
}

public
#if NET6_0 || NETSTANDARD2_1
ValueTask
#elif NETSTANDARD2_0
Task
#else
#error Target Framework not supported
#endif
CloneFileAsync(string source, string destination, CloneFlags cloneFlags, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}

public void ClearFilesystemCache()
{
}
Expand Down
23 changes: 0 additions & 23 deletions lib/Windows/WindowsCopyOnWriteFilesystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;

namespace Microsoft.CopyOnWrite.Windows;
Expand Down Expand Up @@ -79,19 +77,6 @@ public void CloneFile(string source, string destination)
// Also see https://github.com/0xbadfca11/reflink/blob/master/reflink.cpp
// (discussion in http://blog.dewin.me/2017/02/under-hood-how-does-refs-block-cloning.html).
public void CloneFile(string source, string destination, CloneFlags cloneFlags)
{
CloneFileAsync(source, destination, cloneFlags, CancellationToken.None).GetAwaiter().GetResult();
}

public
#if NET6_0 || NETSTANDARD2_1
ValueTask
#elif NETSTANDARD2_0
Task
#else
#error Target Framework not supported
#endif
CloneFileAsync(string source, string destination, CloneFlags cloneFlags, CancellationToken cancellationToken)
{
(string resolvedSource, bool sourceOk) = ResolvePathAndEnsureDriveLetterVolume(source, cloneFlags.HasFlag(CloneFlags.PathIsFullyResolved));
if (!sourceOk)
Expand Down Expand Up @@ -259,14 +244,6 @@ public void CloneFile(string source, string destination, CloneFlags cloneFlags)
$"Failed to turn off file sparseness with winerror {lastErr} for destination file '{destination}'");
}
}

#if NET6_0 || NETSTANDARD2_1
return ValueTask.CompletedTask;
#elif NETSTANDARD2_0
return Task.CompletedTask;
#else
#error Target Framework not supported
#endif
}

// Separate method to avoid error creating DUPLICATE_EXTENTS_DATA on stack in async method.
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/Windows/CopyOnWriteTests_Windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public async Task TempDirValidateCloneFileFailureIfNtfs()

try
{
await cow.CloneFileAsync(sourceFilePath, destFilePath, CloneFlags.None, CancellationToken.None);
cow.CloneFile(sourceFilePath, destFilePath, CloneFlags.None);
Assert.Fail("Expected exception on NTFS");
}
catch (NotSupportedException ex)
Expand Down Expand Up @@ -378,11 +378,11 @@ public async Task VerifyMatchingSparseness()
Assert.AreEqual("This file is NOT set as sparse", fsutilResult.Output.Trim());
string nonSparseFileClone = Path.Combine(refs.TestRootDir, "nonSparseFileClone");

await cow.CloneFileAsync(sparseFile, sparseFileClone, CloneFlags.DestinationMustMatchSourceSparseness, CancellationToken.None);
cow.CloneFile(sparseFile, sparseFileClone, CloneFlags.DestinationMustMatchSourceSparseness);
fsutilResult = ProcessExecutionUtilities.RunAndCaptureOutput("fsutil", $"sparse queryFlag {sparseFileClone}");
Assert.AreEqual("This file is set as sparse", fsutilResult.Output.Trim());

await cow.CloneFileAsync(nonSparseFile, nonSparseFileClone, CloneFlags.DestinationMustMatchSourceSparseness, CancellationToken.None);
cow.CloneFile(nonSparseFile, nonSparseFileClone, CloneFlags.DestinationMustMatchSourceSparseness);
fsutilResult = ProcessExecutionUtilities.RunAndCaptureOutput("fsutil", $"sparse queryFlag {nonSparseFileClone}");
Assert.AreEqual("This file is NOT set as sparse", fsutilResult.Output.Trim());
}
Expand Down

0 comments on commit 59ea2bc

Please sign in to comment.