@@ -84,6 +84,8 @@ APPLICATION_INFO::CreateApplication(IHttpContext& pHttpContext)
8484 return S_OK;
8585 }
8686
87+ std::wstring pExceptionMessage;
88+
8789 try
8890 {
8991 const WebConfigConfigurationSource configurationSource (m_pServer.GetAdminManager (), pHttpApplication);
@@ -138,29 +140,43 @@ APPLICATION_INFO::CreateApplication(IHttpContext& pHttpContext)
138140 }
139141 return S_OK;
140142 }
141- catch (const ConfigurationLoadException &ex)
142- {
143- EventLog::Error (
144- ASPNETCORE_CONFIGURATION_LOAD_ERROR,
145- ASPNETCORE_CONFIGURATION_LOAD_ERROR_MSG,
146- ex.get_message ().c_str ());
147- }
148143 catch (...)
149144 {
150145 OBSERVE_CAUGHT_EXCEPTION ();
146+ pExceptionMessage = CaughtExceptionToString ();
151147 EventLog::Error (
152148 ASPNETCORE_CONFIGURATION_LOAD_ERROR,
153149 ASPNETCORE_CONFIGURATION_LOAD_ERROR_MSG,
154- L" " );
150+ pExceptionMessage. c_str () );
155151 }
156152
153+ ErrorContext errorContext;
154+ errorContext.statusCode = 500i16;
155+ errorContext.subStatusCode = 30i16;
156+ errorContext.generalErrorType = " ASP.NET Core app failed to start - An exception was thrown during startup" ;
157+ if (GetLastError () == ERROR_ACCESS_DENIED)
158+ {
159+ errorContext.errorReason = " Ensure the application pool process model has write permissions to the shadow copy directory" ;
160+ }
161+ // TODO: Depend on show detailed errors or nah?
162+ errorContext.detailedErrorContent = to_multi_byte_string (pExceptionMessage, CP_UTF8);
163+ auto page = ANCM_ERROR_PAGE;
164+
165+
166+ auto responseContent = FILE_UTILITY::GetHtml (g_hServerModule,
167+ page,
168+ errorContext.statusCode ,
169+ errorContext.subStatusCode ,
170+ errorContext.generalErrorType ,
171+ errorContext.errorReason ,
172+ errorContext.detailedErrorContent );
157173 m_pApplication = make_application<ServerErrorApplication>(
158174 pHttpApplication,
159175 E_FAIL,
160176 false /* disableStartupPage */ ,
161- " " /* responseContent */ ,
162- 500i16 /* statusCode */ ,
163- 0i16 /* subStatusCode */ ,
177+ responseContent /* responseContent */ ,
178+ errorContext. statusCode /* statusCode */ ,
179+ errorContext. subStatusCode /* subStatusCode */ ,
164180 " Internal Server Error" );
165181
166182 return S_OK;
@@ -193,7 +209,26 @@ APPLICATION_INFO::TryCreateApplication(IHttpContext& pHttpContext, const ShimOpt
193209 }
194210 }
195211
196- auto shadowCopyPath = HandleShadowCopy (options, pHttpContext);
212+ std::filesystem::path shadowCopyPath;
213+
214+ // Only support shadow copying for IIS.
215+ if (options.QueryShadowCopyEnabled () && !m_pServer.IsCommandLineLaunch ())
216+ {
217+ try
218+ {
219+ shadowCopyPath = HandleShadowCopy (options, pHttpContext, error);
220+ }
221+ catch (...)
222+ {
223+ OBSERVE_CAUGHT_EXCEPTION ();
224+ throw ;
225+ }
226+
227+ if (shadowCopyPath.empty ())
228+ {
229+ return E_FAIL;
230+ }
231+ }
197232
198233 RETURN_IF_FAILED (m_handlerResolver.GetApplicationFactory (*pHttpContext.GetApplication (), shadowCopyPath, m_pApplicationFactory, options, error));
199234 LOG_INFO (L" Creating handler application" );
@@ -275,74 +310,107 @@ APPLICATION_INFO::ShutDownApplication(const bool fServerInitiated)
275310 * we will start a thread that deletes all other folders in that directory.
276311 */
277312std::filesystem::path
278- APPLICATION_INFO::HandleShadowCopy (const ShimOptions& options, IHttpContext& pHttpContext)
313+ APPLICATION_INFO::HandleShadowCopy (const ShimOptions& options, IHttpContext& pHttpContext, ErrorContext& error )
279314{
280- std::filesystem::path shadowCopyPath;
315+ std::filesystem::path shadowCopyPath = options.QueryShadowCopyDirectory ();
316+ std::wstring physicalPath = pHttpContext.GetApplication ()->GetApplicationPhysicalPath ();
281317
282- // Only support shadow copying for IIS .
283- if (options. QueryShadowCopyEnabled () && !m_pServer. IsCommandLineLaunch ())
318+ // Make shadow copy path absolute .
319+ if (!shadowCopyPath. is_absolute ())
284320 {
285- shadowCopyPath = options. QueryShadowCopyDirectory ( );
286- std::wstring physicalPath = pHttpContext. GetApplication ()-> GetApplicationPhysicalPath ();
321+ shadowCopyPath = std::filesystem::absolute ( std::filesystem::path (physicalPath) / shadowCopyPath );
322+ }
287323
288- // Make shadow copy path absolute.
289- if (!shadowCopyPath.is_absolute ())
290- {
291- shadowCopyPath = std::filesystem::absolute (std::filesystem::path (physicalPath) / shadowCopyPath);
292- }
324+ // The shadow copy directory itself isn't copied to directly.
325+ // Instead subdirectories with numerically increasing names are created.
326+ // This is because on shutdown, the app itself will still have all dlls loaded,
327+ // meaning we can't copy to the same subdirectory. Therefore, on shutdown,
328+ // we create a directory that is one larger than the previous largest directory number.
329+ auto directoryName = 0 ;
330+ std::string directoryNameStr = " 0" ;
331+ auto shadowCopyBaseDirectory = std::filesystem::directory_entry (shadowCopyPath);
332+ if (!shadowCopyBaseDirectory.exists ())
333+ {
334+ LOG_INFOF (L" Attempting to Create Directory" );
293335
294- // The shadow copy directory itself isn't copied to directly.
295- // Instead subdirectories with numerically increasing names are created.
296- // This is because on shutdown, the app itself will still have all dlls loaded,
297- // meaning we can't copy to the same subdirectory. Therefore, on shutdown,
298- // we create a directory that is one larger than the previous largest directory number.
299- auto directoryName = 0 ;
300- std::string directoryNameStr = " 0" ;
301- auto shadowCopyBaseDirectory = std::filesystem::directory_entry (shadowCopyPath);
302- if (!shadowCopyBaseDirectory.exists ())
336+ auto ret = CreateDirectory (shadowCopyBaseDirectory.path ().wstring ().c_str (), nullptr );
337+ if (!ret)
303338 {
304- CreateDirectory (shadowCopyBaseDirectory.path ().wstring ().c_str (), nullptr );
339+ LOG_ERRORF (L" Failed to create shadow copy base directory %ls. Error: %d" ,
340+ shadowCopyBaseDirectory.path ().c_str (),
341+ GetLastError ());
305342 }
343+ }
306344
307- for (auto & entry : std::filesystem::directory_iterator (shadowCopyPath))
345+ for (auto & entry : std::filesystem::directory_iterator (shadowCopyPath))
346+ {
347+ if (entry.is_directory ())
308348 {
309- if (entry. is_directory ())
349+ try
310350 {
311- try
312- {
313- auto tempDirName = entry.path ().filename ().string ();
314- int intFileName = std::stoi (tempDirName);
315- if (intFileName > directoryName)
316- {
317- directoryName = intFileName;
318- directoryNameStr = tempDirName;
319- }
320- }
321- catch (...)
351+ auto tempDirName = entry.path ().filename ().string ();
352+ int intFileName = std::stoi (tempDirName);
353+ if (intFileName > directoryName)
322354 {
323- OBSERVE_CAUGHT_EXCEPTION () ;
324- // Ignore any folders that can't be converted to an int.
355+ directoryName = intFileName ;
356+ directoryNameStr = tempDirName;
325357 }
326358 }
359+ catch (...)
360+ {
361+ OBSERVE_CAUGHT_EXCEPTION ();
362+ // Ignore any folders that can't be converted to an int.
363+ }
327364 }
365+ }
366+
367+ int copiedFileCount = 0 ;
368+
369+ shadowCopyPath = shadowCopyPath / directoryNameStr;
370+ LOG_INFOF (L" Copying from %ls to shadow copy directory %ls." , physicalPath.c_str (), shadowCopyPath.c_str ());
328371
329- int copiedFileCount = 0 ;
372+ // Avoid using canonical for shadowCopyBaseDirectory
373+ // It could expand to a network drive, or an expanded link folder path
374+ // We already made it an absolute path relative to the physicalPath above
375+ try {
376+ // CopyToDirectory throws exception on failure, therefore don't need to check return value
377+ Environment::CopyToDirectory (physicalPath, shadowCopyPath, options.QueryCleanShadowCopyDirectory (), shadowCopyBaseDirectory.path (), copiedFileCount);
378+ }
379+ catch (const std::system_error& ex)
380+ {
381+ auto exWideString = to_wide_string (ex.what (), CP_ACP);
382+
383+ std::wstring logMessage = format (L" Failed to copy files from %s to shadow copy directory %s. Error: %s" ,
384+ physicalPath.c_str (),
385+ shadowCopyPath.c_str (),
386+ exWideString.c_str ());
330387
331- shadowCopyPath = shadowCopyPath / directoryNameStr;
332- LOG_INFOF (L" Copying to shadow copy directory %ls." , shadowCopyPath.c_str ());
388+ LOG_ERRORF (L" %ls" , logMessage.c_str ());
389+ EventLog::Error (ASPNETCORE_CONFIGURATION_LOAD_ERROR,
390+ ASPNETCORE_CONFIGURATION_LOAD_ERROR_MSG,
391+ logMessage.c_str ());
333392
334- // Avoid using canonical for shadowCopyBaseDirectory
335- // It could expand to a network drive, or an expanded link folder path
336- // We already made it an absolute path relative to the physicalPath above
337- HRESULT hr = Environment::CopyToDirectory (physicalPath, shadowCopyPath, options.QueryCleanShadowCopyDirectory (), shadowCopyBaseDirectory.path (), copiedFileCount);
393+ // TODO: Better substatus code
394+ error.statusCode = 500i16;
395+ error.subStatusCode = 30i16;
338396
339- LOG_INFOF (L" Finished copying %d files to shadow copy directory %ls." , copiedFileCount, shadowCopyBaseDirectory.path ().c_str ());
397+ std::string errorMessage = " Failed to copy to shadow copy directory" ;
398+ auto exceptionCode = ex.code ().value ();
399+ if (exceptionCode == ERROR_ACCESS_DENIED || exceptionCode == ERROR_PATH_NOT_FOUND)
400+ {
401+ errorMessage = " No permissions to shadow copy directory" ;
402+ }
340403
341- if (hr != S_OK)
404+ error.generalErrorType = format (" ASP.NET Core app failed to start - %s" , errorMessage.c_str ());
405+ error.errorReason = format (" Ensure the application pool process model has write permissions to the shadow copy directory %ls" ,
406+ shadowCopyPath.c_str ());
407+ if (options.QueryShowDetailedErrors ())
342408 {
343- return std::wstring ();
409+ error. detailedErrorContent = ex. what ();
344410 }
411+ return std::wstring ();
345412 }
346413
414+ LOG_INFOF (L" Finished copying %d files to shadow copy directory %ls." , copiedFileCount, shadowCopyBaseDirectory.path ().c_str ());
347415 return shadowCopyPath;
348416}
0 commit comments