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