提交 d9457811 编写于 作者: S Swaroop Sridhar 提交者: GitHub

HostModel: Retry ResourceUpdate on Win32 File-Lock error (dotnet/core-setup#7665)

* HostModel: Retry ResourceUpdate on Win32 File-Lock error

On Win32 PE files, the SDK copies resources on the AppHost binary
from the intermediate assembly.

This update is performed using native Win32 operations.
This change retries the resource update if the operation fails
because the file is locked (say because of AntiVirus scan).

This change is similar to: https://github.com/dotnet/core-setup/pull/7617
which handles failures in managed code via IOException.

The Retry logic is factored out to the RetryUtil class. Currently the
HostWriter is the only client for the class. But it is made public
because other components (like Bundler) may use it in future.


Commit migrated from https://github.com/dotnet/core-setup/commit/6f3ead36128f5ef3287a847fe9f4cf0d1177a057
上级 832247d2
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Microsoft.NET.HostModel
{
/// <summary>
/// Represents an exception thrown because of a Win32 error
/// </summary>
public class HResultException : Exception
{
public readonly int Win32HResult;
public HResultException(int hResult) : base(hResult.ToString("X4"))
{
Win32HResult = hResult;
}
}
}
......@@ -21,32 +21,6 @@ public static class HostWriter
private const string AppBinaryPathPlaceholder = "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2";
private readonly static byte[] AppBinaryPathPlaceholderSearchValue = Encoding.UTF8.GetBytes(AppBinaryPathPlaceholder);
/// <summary>
/// The HostModel implements several services for updating the AppHost DLL.
/// These updates involve multiple file open/close operations.
/// An Antivirus scanner may intercept in-between and lock the file,
/// causing the operations to fail with IO-Error.
/// So, the operations are retried a few times on IOException.
/// </summary>
/// <param name="func">The action to retry on IO-Error</param>
private static void RetryOnIOError(Action func)
{
uint numberOfRetries = 500;
for (uint i = 1; i <= numberOfRetries; i++)
{
try
{
func();
break;
}
catch (IOException) when (i < numberOfRetries)
{
Thread.Sleep(100);
}
}
}
/// <summary>
/// Create an AppHost with embedded configuration of app binary location
/// </summary>
......@@ -69,11 +43,11 @@ private static void RetryOnIOError(Action func)
}
BinaryUtils.CopyFile(appHostSourceFilePath, appHostDestinationFilePath);
bool appHostIsPEImage = false;
void RewriteAppHost()
{
// Re-write the destination apphost with the proper contents.
bool appHostIsPEImage = false;
using (var memoryMappedFile = MemoryMappedFile.CreateFromFile(appHostDestinationFilePath))
{
using (MemoryMappedViewAccessor accessor = memoryMappedFile.CreateViewAccessor())
......@@ -93,7 +67,10 @@ void RewriteAppHost()
}
}
}
}
void UpdateResources()
{
if (assemblyToCopyResorcesFrom != null && appHostIsPEImage)
{
if (ResourceUpdater.IsSupportedOS())
......@@ -112,10 +89,12 @@ void RewriteAppHost()
try
{
RetryOnIOError(RewriteAppHost);
RetryUtil.RetryOnIOError(RewriteAppHost);
RetryUtil.RetryOnWin32Error(UpdateResources);
// Memory-mapped write does not updating last write time
RetryOnIOError(() => File.SetLastWriteTimeUtc(appHostDestinationFilePath, DateTime.UtcNow));
RetryUtil.RetryOnIOError(() => File.SetLastWriteTimeUtc(appHostDestinationFilePath, DateTime.UtcNow));
}
catch (Exception ex)
{
......@@ -154,13 +133,15 @@ void RewriteAppHost()
};
// Re-write the destination apphost with the proper contents.
RetryOnIOError(() => BinaryUtils.SearchAndReplace(appHostPath,
bundleHeaderPlaceholder,
BitConverter.GetBytes(bundleHeaderOffset),
pad0s:false));
RetryUtil.RetryOnIOError(() =>
BinaryUtils.SearchAndReplace(appHostPath,
bundleHeaderPlaceholder,
BitConverter.GetBytes(bundleHeaderOffset),
pad0s:false));
// Memory-mapped write does not updating last write time
RetryOnIOError(() => File.SetLastWriteTimeUtc(appHostPath, DateTime.UtcNow));
RetryUtil.RetryOnIOError(() =>
File.SetLastWriteTimeUtc(appHostPath, DateTime.UtcNow));
}
/// <summary>
......@@ -197,7 +178,7 @@ void FindBundleHeader()
}
}
RetryOnIOError(FindBundleHeader);
RetryUtil.RetryOnIOError(FindBundleHeader);
bundleHeaderOffset = headerOffset;
return headerOffset != 0;
......
......@@ -2,7 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
......@@ -424,13 +423,6 @@ public ResourceNotAvailableException(string message) : base(message)
}
}
private class HResultException : Exception
{
public HResultException(int hResult) : base(hResult.ToString("X4"))
{
}
}
private static void ThrowExceptionForLastWin32Error()
{
throw new HResultException(Marshal.GetHRForLastWin32Error());
......
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Text;
using System.Threading;
namespace Microsoft.NET.HostModel
{
/// <summary>
/// HostModel library implements several services for updating the AppHost DLL.
/// These updates involve multiple file open/close operations.
/// An Antivirus scanner may intercept in-between and lock the file,
/// causing the operations to fail with IO-Error.
/// So, the operations are retried a few times on failures such as
/// - IOException
/// - Failure with Win32 errors indicating file-lock
/// </summary>
public static class RetryUtil
{
public const int NumberOfRetries = 500;
public const int NumMilliSecondsToWait = 100;
public static void RetryOnIOError(Action func)
{
for (int i = 1; i <= NumberOfRetries; i++)
{
try
{
func();
break;
}
catch (IOException) when (i < NumberOfRetries)
{
Thread.Sleep(NumMilliSecondsToWait);
}
}
}
public static void RetryOnWin32Error(Action func)
{
bool IsWin32FileLockError(int hresult)
{
// Error codes are defined in winerror.h
const int ErrorLockViolation = 33;
const int ErrorDriveLocked = 108;
// The error code is stored in the lowest 16 bits of the HResult
int errorCode = hresult & 0xffff;
return errorCode == ErrorLockViolation || errorCode == ErrorDriveLocked;
}
for (int i = 1; i <= NumberOfRetries; i++)
{
try
{
func();
break;
}
catch (HResultException hrex)
when (i < NumberOfRetries && IsWin32FileLockError(hrex.Win32HResult))
{
Thread.Sleep(NumMilliSecondsToWait);
}
}
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册