Skip to content

Usage of hard or symbolic linking leads to NuGet cache corruption #8273

@marcin-krystianc

Description

@marcin-krystianc

Issue Description

MSBuild can use hard or symbolic links to avoid excessive file copies on disk. It is a good method to speed-up builds but at the same time, it is very easy to silently corrupt the NuGet cache.
I have seen that this issue has been mentioned several times already, but it was never explained how and when exactly the NuGet cache gets corrupted:

Related issues:

Steps to Reproduce

  1. Build an executable project with hard or symbolic links enabled
  2. Update the version of referenced NuGet package
  3. Build the application again, but this time, without using hard/symbolic links.
    MSBuild will try to update dependencies in the application's output folder. Sadly, instead of replacing existing links, it replaces actual files in NuGet cache (thus corrupting it).
  • hard-links
dotnet nuget locals --clear all
dotnet new console
dotnet add package newtonsoft.json -v 13.0.1
dotnet build /p:CreateHardLinksForCopyLocalIfPossible=true
dotnet add package newtonsoft.json -v 13.0.2
dotnet build
  • symbolic-links
dotnet nuget locals --clear all
dotnet new console
dotnet add package newtonsoft.json -v 13.0.1
dotnet build /p:CreateSymbolicLinksForCopyLocalIfPossible=true
dotnet add package newtonsoft.json -v 13.0.2
dotnet build

In both cases file newtonsoft.json\13.0.1\lib\netstandard2.0\Newtonsoft.Json.dll is silently replaced with newtonsoft.json\13.0.2\lib\net6.0\Newtonsoft.Json.dll:

Expected Behavior

Files in the NuGet cache remain untouched.

Actual Behavior

Files in a NuGet package are silently replaced with files from another version.

Analysis

Both Windows and Linux systems are affected, also it doesn't matter whether hard or symbolic links are used. The problem is that File.Copy operation, instead of replacing the link, replaces the file that the link is linking to. To safely replace a link with a different file or link, File.Delete needs to be called first. Unfortunately, MSBuild calls File.Delete only when the usage of hard or symbolic links is requested. When the build doesn't use hard or symbolic links, then the File.Delete is not called.

Versions & Configurations

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugneeds-triageHave yet to determine what bucket this goes in.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions