Add a reference counter to avoid AV in case web.config was changed
This commit is contained in:
Sourabh Shirhatti 2016-09-20 15:47:50 -07:00
Родитель dff0db80ca
Коммит f558c5de99
10 изменённых файлов: 170 добавлений и 134 удалений

Просмотреть файл

@ -202,12 +202,6 @@ public:
return m_pAppOfflineHtm;
}
STRU*
QueryApplicationPhysicalPath()
{
return &m_strAppPhysicalPath;
}
~APPLICATION();
HRESULT

Просмотреть файл

@ -39,17 +39,9 @@ public:
HRESULT
GetApplication(
_In_ IHttpContext* pContext,
_In_ LPCWSTR pszApplication,
_Out_ APPLICATION ** ppApplication
);
static
VOID
RecycleOnFileChange(
APPLICATION_MANAGER* manager,
APPLICATION* application
);
HRESULT
RecycleApplication(
_In_ LPCWSTR pszApplication

Просмотреть файл

@ -4,6 +4,7 @@
#pragma once
#define FILE_WATCHER_SHUTDOWN_KEY (ULONG_PTR)(-1)
#define FILE_WATCHER_ENTRY_BUFFER_SIZE 4096
#ifndef CONTAINING_RECORD
//
// Calculate the address of the base of the structure given its type, and an
@ -53,14 +54,12 @@ public:
private:
HANDLE m_hCompletionPort;
HANDLE m_hChangeNotificationThread;
CRITICAL_SECTION m_csSyncRoot;
};
class FILE_WATCHER_ENTRY
{
public:
FILE_WATCHER_ENTRY(FILE_WATCHER * pFileMonitor);
virtual ~FILE_WATCHER_ENTRY();
OVERLAPPED _overlapped;
@ -72,6 +71,33 @@ public:
_In_ HANDLE hImpersonationToken
);
VOID
ReferenceFileWatcherEntry() const
{
InterlockedIncrement(&_cRefs);
}
VOID
DereferenceFileWatcherEntry() const
{
if (InterlockedDecrement(&_cRefs) == 0)
{
delete this;
}
}
BOOL
QueryIsValid() const
{
return _fIsValid;
}
VOID
MarkEntryInValid()
{
_fIsValid = FALSE;
}
HRESULT Monitor();
VOID StopMonitor();
@ -83,6 +109,8 @@ public:
);
private:
virtual ~FILE_WATCHER_ENTRY();
DWORD _dwSignature;
BUFFER _buffDirectoryChanges;
HANDLE _hImpersonationToken;
@ -92,5 +120,7 @@ private:
STRU _strFileName;
STRU _strDirectoryName;
LONG _lStopMonitorCalled;
mutable LONG _cRefs;
BOOL _fIsValid;
SRWLOCK _srwLock;
};

Просмотреть файл

@ -7,12 +7,12 @@
#define IDS_SERVER_ERROR 1001
#define ASPNETCORE_EVENT_MSG_BUFFER_SIZE 256
#define ASPNETCORE_EVENT_PROCESS_START_SUCCESS_MSG L"Process '%d' started successfully and is listening on port '%d'."
#define ASPNETCORE_EVENT_PROCESS_START_SUCCESS_MSG L"Application '%s' started process '%d' successfully and is listening on port '%d'."
#define ASPNETCORE_EVENT_RAPID_FAIL_COUNT_EXCEEDED_MSG L"Maximum rapid fail count per minute of '%d' exceeded."
#define ASPNETCORE_EVENT_PROCESS_START_INTERNAL_ERROR_MSG L"Failed to parse processPath and arguments due to internal error, ErrorCode = '0x%x'."
#define ASPNETCORE_EVENT_PROCESS_START_POSTCREATE_ERROR_MSG L"Process was created with commandline '%s'but failed to get its status, ErrorCode = '0x%x'."
#define ASPNETCORE_EVENT_PROCESS_START_ERROR_MSG L"Failed to start process with commandline '%s', ErrorCode = '0x%x'."
#define ASPNETCORE_EVENT_PROCESS_START_WRONGPORT_ERROR_MSG L"Process was created with commandline '%s' but did not listen on the given port '%d'"
#define ASPNETCORE_EVENT_PROCESS_START_NOTREADY_ERROR_MSG L"Process was created with commandline '%s' but either crashed or did not reponse within given time or did not listen on the given port '%d', ErrorCode = '0x%x'"
#define ASPNETCORE_EVENT_PROCESS_START_INTERNAL_ERROR_MSG L"Application '%s' failed to parse processPath and arguments due to internal error, ErrorCode = '0x%x'."
#define ASPNETCORE_EVENT_PROCESS_START_POSTCREATE_ERROR_MSG L"Application '%s' with physical root '%s' created process with commandline '%s'but failed to get its status, ErrorCode = '0x%x'."
#define ASPNETCORE_EVENT_PROCESS_START_ERROR_MSG L"Application '%s' with physical root '%s' failed to start process with commandline '%s', ErrorCode = '0x%x : %x."
#define ASPNETCORE_EVENT_PROCESS_START_WRONGPORT_ERROR_MSG L"Application '%s' with physical root '%s' created process with commandline '%s' but failed to listen on the given port '%d'"
#define ASPNETCORE_EVENT_PROCESS_START_NOTREADY_ERROR_MSG L"Application '%s' with physical root '%s' created process with commandline '%s' but either crashed or did not reponse or did not listen on the given port '%d', ErrorCode = '0x%x'"
#define ASPNETCORE_EVENT_INVALID_STDOUT_LOG_FILE_MSG L"Warning: Could not create stdoutLogFile %s, ErrorCode = %d."
#define ASPNETCORE_EVENT_GRACEFUL_SHUTDOWN_FAILURE_MSG L"Failed to gracefully shutdown process '%d'."

Просмотреть файл

@ -7,8 +7,11 @@ APPLICATION::~APPLICATION()
{
if (m_pFileWatcherEntry != NULL)
{
// Mark the entry as invalid,
// StopMonitor will close the file handle and trigger a FCN
// the entry will delete itself when processing this FCN
m_pFileWatcherEntry->MarkEntryInValid();
m_pFileWatcherEntry->StopMonitor();
delete m_pFileWatcherEntry;
m_pFileWatcherEntry = NULL;
}
@ -76,7 +79,7 @@ Finished:
{
if (m_pFileWatcherEntry != NULL)
{
delete m_pFileWatcherEntry;
m_pFileWatcherEntry->DereferenceFileWatcherEntry();
m_pFileWatcherEntry = NULL;
}

Просмотреть файл

@ -8,7 +8,6 @@ APPLICATION_MANAGER* APPLICATION_MANAGER::sm_pApplicationManager = NULL;
HRESULT
APPLICATION_MANAGER::GetApplication(
_In_ IHttpContext* pContext,
_In_ LPCWSTR pszApplication,
_Out_ APPLICATION ** ppApplication
)
{
@ -16,11 +15,15 @@ APPLICATION_MANAGER::GetApplication(
APPLICATION *pApplication = NULL;
APPLICATION_KEY key;
BOOL fExclusiveLock = FALSE;
PCWSTR pszApplicationId = NULL;
*ppApplication = NULL;
DBG_ASSERT(pContext != NULL);
DBG_ASSERT(pContext->GetApplication() != NULL);
pszApplicationId = pContext->GetApplication()->GetApplicationId();
hr = key.Initialize(pszApplication);
hr = key.Initialize(pszApplicationId);
if (FAILED(hr))
{
goto Finished;
@ -50,7 +53,7 @@ APPLICATION_MANAGER::GetApplication(
goto Finished;
}
hr = pApplication->Initialize(this, pszApplication, pContext->GetApplication()->GetApplicationPhysicalPath());
hr = pApplication->Initialize(this, pszApplicationId, pContext->GetApplication()->GetApplicationPhysicalPath());
if (FAILED(hr))
{
goto Finished;
@ -88,14 +91,6 @@ Finished:
return hr;
}
VOID
APPLICATION_MANAGER::RecycleOnFileChange(
APPLICATION_MANAGER*,
APPLICATION*
)
{
g_pHttpServer->RecycleProcess(L"Asp.Net Core Module Recycle Process on File Change Notification");
}
HRESULT
APPLICATION_MANAGER::RecycleApplication(

Просмотреть файл

@ -79,7 +79,9 @@ ASPNETCORE_CONFIG::GetConfig(
}
else
{
hr = pAspNetCoreConfig->QueryApplicationPath()->Copy(pHttpApplication->GetAppConfigPath());
// set appliction info here instead of inside Populate()
// as the destructor will delete the backend process
hr = pAspNetCoreConfig->QueryApplicationPath()->Copy(pHttpApplication->GetApplicationId());
if (FAILED(hr))
{
goto Finished;
@ -382,6 +384,7 @@ ASPNETCORE_CONFIG::Populate(
}
pcszEnvName = mszEnvNames.Next(pcszEnvName);
}
//
// let's disable this feature for now
//

Просмотреть файл

@ -7,12 +7,15 @@ FILE_WATCHER::FILE_WATCHER() :
m_hCompletionPort(NULL),
m_hChangeNotificationThread(NULL)
{
InitializeCriticalSection(&this->m_csSyncRoot);
}
FILE_WATCHER::~FILE_WATCHER()
{
DeleteCriticalSection(&this->m_csSyncRoot);
if (m_hChangeNotificationThread != NULL)
{
CloseHandle(m_hChangeNotificationThread);
m_hChangeNotificationThread = NULL;
}
}
HRESULT
@ -76,29 +79,34 @@ Win32 error
--*/
{
FILE_WATCHER * pFileMonitor;
BOOL fRet = FALSE;
BOOL fSuccess = FALSE;
DWORD cbCompletion = 0;
OVERLAPPED * pOverlapped = NULL;
DWORD dwErrorStatus;
ULONG_PTR completionKey;
pFileMonitor = (FILE_WATCHER*)pvArg;
DBG_ASSERT(pFileMonitor != NULL);
while (TRUE)
{
fRet = GetQueuedCompletionStatus(
fSuccess = GetQueuedCompletionStatus(
pFileMonitor->m_hCompletionPort,
&cbCompletion,
&completionKey,
&pOverlapped,
INFINITE);
dwErrorStatus = fRet ? ERROR_SUCCESS : GetLastError();
DBG_ASSERT(fSuccess);
DebugPrint(1, "FILE_WATCHER::ChangeNotificationThread");
dwErrorStatus = fSuccess ? ERROR_SUCCESS : GetLastError();
if (completionKey == FILE_WATCHER_SHUTDOWN_KEY)
{
continue;
}
DBG_ASSERT(pOverlapped != NULL);
if (pOverlapped != NULL)
{
FileWatcherCompletionRoutine(
@ -138,10 +146,26 @@ None
{
FILE_WATCHER_ENTRY * pMonitorEntry;
pMonitorEntry = CONTAINING_RECORD(pOverlapped, FILE_WATCHER_ENTRY, _overlapped);
pMonitorEntry->DereferenceFileWatcherEntry();
DBG_ASSERT(pMonitorEntry != NULL);
pMonitorEntry->HandleChangeCompletion(dwCompletionStatus,
cbCompletion);
pMonitorEntry->HandleChangeCompletion(dwCompletionStatus, cbCompletion);
if (pMonitorEntry->QueryIsValid())
{
//
// Continue monitoring
//
pMonitorEntry->Monitor();
}
else
{
//
// Marked by application distructor
// Deference the entry to delete it
//
pMonitorEntry->DereferenceFileWatcherEntry();
}
}
@ -150,7 +174,9 @@ FILE_WATCHER_ENTRY::FILE_WATCHER_ENTRY(FILE_WATCHER * pFileMonitor) :
_hDirectory(INVALID_HANDLE_VALUE),
_hImpersonationToken(NULL),
_pApplication(NULL),
_lStopMonitorCalled(0)
_lStopMonitorCalled(0),
_cRefs(1),
_fIsValid(TRUE)
{
_dwSignature = FILE_WATCHER_ENTRY_SIGNATURE;
InitializeSRWLock(&_srwLock);
@ -201,6 +227,12 @@ HRESULT
FILE_NOTIFY_INFORMATION * pNotificationInfo;
BOOL fFileChanged = FALSE;
AcquireSRWLockExclusive(&_srwLock);
if (!_fIsValid)
{
goto Finished;
}
// When directory handle is closed then HandleChangeCompletion
// happens with cbCompletion = 0 and dwCompletionStatus = 0
// From documentation it is not clear if that combination
@ -211,32 +243,31 @@ HRESULT
//
if (_lStopMonitorCalled)
{
hr = S_OK;
goto Finished;
}
//
// There could be a FCN overflow
// Let assume the file got changed instead of checking files
// Othersie we have to cache the file info
//
if (cbCompletion == 0)
{
//
// There could be a FCN overflow
// Let assume the file got changed instead of checking files
// Othersie we have to cache the file info
//
{
fFileChanged = TRUE;
hr = HRESULT_FROM_WIN32(dwCompletionStatus);
}
else
{
pNotificationInfo = (FILE_NOTIFY_INFORMATION*)_buffDirectoryChanges.QueryPtr();
_ASSERT(pNotificationInfo != NULL);
DBG_ASSERT(pNotificationInfo != NULL);
while (pNotificationInfo != NULL)
{
//
// check whether the monitored file got changed
//
if (wcscmp(pNotificationInfo->FileName, _strFileName.QueryStr()) == 0)
if (wcsncmp(pNotificationInfo->FileName,
_strFileName.QueryStr(),
pNotificationInfo->FileNameLength/sizeof(WCHAR)) == 0)
{
fFileChanged = TRUE;
break;
@ -255,13 +286,7 @@ HRESULT
pNotificationInfo->NextEntryOffset);
}
}
RtlZeroMemory(_buffDirectoryChanges.QueryPtr(), _buffDirectoryChanges.QuerySize());
}
//
//continue monitoring
//
StopMonitor();
if (fFileChanged)
{
@ -271,9 +296,8 @@ HRESULT
_pApplication->UpdateAppOfflineFileHandle();
}
hr = Monitor();
Finished:
ReleaseSRWLockExclusive(&_srwLock);
return hr;
}
@ -285,69 +309,23 @@ FILE_WATCHER_ENTRY::Monitor(VOID)
DWORD cbRead;
AcquireSRWLockExclusive(&_srwLock);
ReferenceFileWatcherEntry();
ZeroMemory(&_overlapped, sizeof(_overlapped));
if (_hDirectory != INVALID_HANDLE_VALUE)
{
CloseHandle(_hDirectory);
_hDirectory = INVALID_HANDLE_VALUE;
}
_hDirectory = CreateFileW(_strDirectoryName.QueryStr(),
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
if (_hDirectory == INVALID_HANDLE_VALUE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
if (CreateIoCompletionPort(
_hDirectory,
_pFileMonitor->QueryCompletionPort(),
NULL,
0) == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
//
// Resize change buffer to something "reasonable"
//
fRet = _buffDirectoryChanges.Resize(4096);
if (!fRet)
{
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
goto Finished;
}
fRet = ReadDirectoryChangesW(_hDirectory,
if(!ReadDirectoryChangesW(_hDirectory,
_buffDirectoryChanges.QueryPtr(),
_buffDirectoryChanges.QuerySize(),
FALSE, // watch sub dirs. set to False now as only monitoring app_offline
FALSE, // Watching sub dirs. Set to False now as only monitoring app_offline
FILE_NOTIFY_VALID_MASK & ~FILE_NOTIFY_CHANGE_LAST_ACCESS & ~FILE_NOTIFY_CHANGE_ATTRIBUTES,
&cbRead,
&_overlapped,
NULL);
if (!fRet)
NULL))
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
InterlockedExchange(&_lStopMonitorCalled, 0);
Finished:
ReleaseSRWLockExclusive(&_srwLock);
return hr;
}
VOID
@ -386,7 +364,7 @@ FILE_WATCHER_ENTRY::Create(
pszFileNameToMonitor == NULL ||
pApplication == NULL)
{
_ASSERT(FALSE);
DBG_ASSERT(FALSE);
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
goto Finished;
}
@ -406,6 +384,15 @@ FILE_WATCHER_ENTRY::Create(
goto Finished;
}
//
// Resize change buffer to something "reasonable"
//
if (!_buffDirectoryChanges.Resize(FILE_WATCHER_ENTRY_BUFFER_SIZE))
{
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
goto Finished;
}
if (hImpersonationToken != NULL)
{
fRet = DuplicateHandle(GetCurrentProcess(),
@ -430,6 +417,32 @@ FILE_WATCHER_ENTRY::Create(
_hImpersonationToken = NULL;
}
}
_hDirectory = CreateFileW(
_strDirectoryName.QueryStr(),
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
if (_hDirectory == INVALID_HANDLE_VALUE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
if (CreateIoCompletionPort(
_hDirectory,
_pFileMonitor->QueryCompletionPort(),
NULL,
0) == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
//
// Start monitoring
//

Просмотреть файл

@ -359,18 +359,10 @@ FORWARDING_HANDLER::SetStatusAndHeaders(
DWORD headerIndex = g_pResponseHeaderHash->GetIndex(strHeaderName.QueryStr());
if (headerIndex == UNKNOWN_INDEX)
{
if (_strnicmp(strHeaderName.QueryStr(), "Sec-WebSocket", 13) != 0 )
{
//
// Perf Opt: Avoid setting websocket headers, since IIS websocket module
// will anyways set these later in the pipeline.
//
hr = pResponse->SetHeader(strHeaderName.QueryStr(),
strHeaderValue.QueryStr(),
static_cast<USHORT>(strHeaderValue.QueryCCH()),
FALSE); // fReplace
}
hr = pResponse->SetHeader(strHeaderName.QueryStr(),
strHeaderValue.QueryStr(),
static_cast<USHORT>(strHeaderValue.QueryCCH()),
FALSE); // fReplace
}
else
{
@ -1074,7 +1066,6 @@ VOID
}
hr = pApplicationManager->GetApplication( m_pW3Context,
m_pW3Context->GetApplication()->GetAppConfigPath(),
&m_pApplication );
if (FAILED(hr))
{

Просмотреть файл

@ -129,6 +129,7 @@ SERVER_PROCESS::StartProcess(
WCHAR* pszPath = NULL;
WCHAR pszFullPath[_MAX_PATH];
LPCWSTR apsz[1];
PCWSTR pszAppPath = NULL;
GetStartupInfoW(&startupInfo);
@ -243,8 +244,6 @@ SERVER_PROCESS::StartProcess(
struApplicationId.Copy( L"ASPNETCORE_APPL_PATH=" );
PCWSTR pszAppPath = NULL;
// let's find the app path. IIS does not support nested sites
// we can seek for the fourth '/' if it exits
// MACHINE/WEBROOT/APPHOST/<site>/<app>.
@ -277,7 +276,7 @@ SERVER_PROCESS::StartProcess(
mszNewEnvironment.Append( struGuidEnv );
pszRootApplicationPath = context->GetRootContext()->GetApplication()->GetApplicationPhysicalPath();
pszRootApplicationPath = context->GetApplication()->GetApplicationPhysicalPath();
//
// generate process command line.
@ -486,8 +485,11 @@ SERVER_PROCESS::StartProcess(
// don't check return code as we already in error report
strEventMsg.SafeSnwprintf(
ASPNETCORE_EVENT_PROCESS_START_ERROR_MSG,
pszAppPath,
pszRootApplicationPath,
finalCommandLine.QueryStr(),
hr);
hr,
0);
goto Finished;
}
@ -537,8 +539,11 @@ SERVER_PROCESS::StartProcess(
hr = E_FAIL;
strEventMsg.SafeSnwprintf(
ASPNETCORE_EVENT_PROCESS_START_ERROR_MSG,
pszAppPath,
pszRootApplicationPath,
finalCommandLine.QueryStr(),
hr);
hr,
processStatus);
goto Finished;
}
}
@ -632,6 +637,8 @@ SERVER_PROCESS::StartProcess(
hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
strEventMsg.SafeSnwprintf(
ASPNETCORE_EVENT_PROCESS_START_NOTREADY_ERROR_MSG,
pszAppPath,
pszRootApplicationPath,
finalCommandLine.QueryStr(),
m_dwPort,
hr);
@ -649,6 +656,8 @@ SERVER_PROCESS::StartProcess(
fReady = FALSE;
strEventMsg.SafeSnwprintf(
ASPNETCORE_EVENT_PROCESS_START_WRONGPORT_ERROR_MSG,
pszAppPath,
pszRootApplicationPath,
finalCommandLine.QueryStr(),
m_dwPort,
hr);
@ -678,6 +687,8 @@ SERVER_PROCESS::StartProcess(
{
strEventMsg.SafeSnwprintf(
ASPNETCORE_EVENT_PROCESS_START_NOTREADY_ERROR_MSG,
pszAppPath,
pszRootApplicationPath,
finalCommandLine.QueryStr(),
m_dwPort,
hr);
@ -724,6 +735,7 @@ SERVER_PROCESS::StartProcess(
if (SUCCEEDED(strEventMsg.SafeSnwprintf(
ASPNETCORE_EVENT_PROCESS_START_SUCCESS_MSG,
pszAppPath,
m_dwProcessId,
m_dwPort)))
{
@ -754,11 +766,14 @@ Finished:
{
if (!fDonePrepareCommandLine)
strEventMsg.SafeSnwprintf(
pszAppPath,
ASPNETCORE_EVENT_PROCESS_START_INTERNAL_ERROR_MSG,
hr);
else
strEventMsg.SafeSnwprintf(
ASPNETCORE_EVENT_PROCESS_START_POSTCREATE_ERROR_MSG,
pszAppPath,
pszRootApplicationPath,
finalCommandLine.QueryStr(),
hr);
}