Single file: Guard against partial cleanup of extracted files (#32649)
authorSwaroop Sridhar <Swaroop.Sridhar@microsoft.com>
Tue, 25 Feb 2020 21:21:20 +0000 (13:21 -0800)
committerGitHub <noreply@github.com>
Tue, 25 Feb 2020 21:21:20 +0000 (13:21 -0800)
commitce23ff90b71b55d0be5d1a90d86b59500e72c4ed
treef4a81aec880ef60e91392cfc8eac9e7bfc3cbafa
parent1961f80f1f6c3e9017c37d68cf5cdeabc331b0b2
Single file: Guard against partial cleanup of extracted files (#32649)

This change fixes https://github.com/dotnet/runtime/issues/3778
This change is mainly targeted to be servicing fix for .net core 3.1.

When executing single-file apps, if a pre-existing extraction exists, the contents of the extraction directory are now verified.
If files are missing, they are recovered.

**Extraction Algorithm**

`ExtractionDir` = `$DOTNET_BUNDLE_EXTRACT_BASE_DIR/<app>/<bundle-id>`
`WorkingDir` = `$DOTNET_BUNDLE_EXTRACT_BASE_DIR/<app>/<process-id>`

If `ExtractionDir` does not exist, then

Create `WorkingDir`, and extract bundle contents within it
Attempt to rename `WorkingDir` as `ExtractionDir`
If the rename succeeds, continue execution of the app
If not, another process completed extraction; Verify and reuse this extraction (as in step 2).
If `ExtractionDir` exists, then verify that it contains all files listed in the manifest.

If all contents are available,
Remove `WorkingDir`
Continue execution of the app, reusing the contents.

If certain files are missing within `ExtractionDir`, then
For each missing file, do the following individually
  Extract the files within `WorkingDir`
  Create sub-paths within `ExtractionDir` if necessary
  Rename the file from `WorkingDir/path/<file>` to `ExtractionDir/path/<file>` unless `ExtractionDir/path/<file>` exists (extracted there by another process in the meantime)
Remove `WorkingDir`
Continue execution of the app.

All of the renames above are done with appropriate retries to circumvent interference from anti-virus apps.

**Startup time impact**
* Console HelloWorld execution time:
    * Framework dependent app: Windows/Linux No measurable difference
    * Self-contained app: Windows: ~10ms additional
    * Self-contained app: Linux: No measurable difference
* Greenshot Startup:
    * Self-contained Windows: No noticiable/measurable difference
* NugetPackageExplorer Startup:
    * Self-contained Windows: No noticiable/measurable difference
src/installer/corehost/cli/apphost/bundle/dir_utils.cpp
src/installer/corehost/cli/apphost/bundle/dir_utils.h
src/installer/corehost/cli/apphost/bundle/extractor.cpp
src/installer/corehost/cli/apphost/bundle/extractor.h
src/installer/corehost/cli/apphost/bundle/runner.cpp
src/installer/test/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs