1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4// ============================================================
5//
6// ApplicationContext.cpp
7//
8
9
10//
11// Implements the ApplicationContext class
12//
13// ============================================================
14
15#ifndef FEATURE_CORESYSTEM
16#define DISABLE_BINDER_DEBUG_LOGGING
17#endif
18
19#include "applicationcontext.hpp"
20#include "stringarraylist.h"
21#include "loadcontext.hpp"
22#include "propertymap.hpp"
23#include "failurecache.hpp"
24#include "assemblyidentitycache.hpp"
25#ifdef FEATURE_VERSIONING_LOG
26#include "debuglog.hpp"
27#endif // FEATURE_VERSIONING_LOG
28#include "utils.hpp"
29#include "variables.hpp"
30#include "ex.h"
31#include "clr/fs/path.h"
32using namespace clr::fs;
33
34namespace BINDER_SPACE
35{
36 STDMETHODIMP ApplicationContext::QueryInterface(REFIID riid,
37 void **ppv)
38 {
39 HRESULT hr = S_OK;
40
41 if (ppv == NULL)
42 {
43 hr = E_POINTER;
44 }
45 else
46 {
47 if (IsEqualIID(riid, IID_IUnknown))
48 {
49 AddRef();
50 *ppv = static_cast<IUnknown *>(this);
51 }
52 else
53 {
54 *ppv = NULL;
55 hr = E_NOINTERFACE;
56 }
57 }
58
59 return hr;
60 }
61
62 STDMETHODIMP_(ULONG) ApplicationContext::AddRef()
63 {
64 return InterlockedIncrement(&m_cRef);
65 }
66
67 STDMETHODIMP_(ULONG) ApplicationContext::Release()
68 {
69 ULONG ulRef = InterlockedDecrement(&m_cRef);
70
71 if (ulRef == 0)
72 {
73 delete this;
74 }
75
76 return ulRef;
77 }
78
79 ApplicationContext::ApplicationContext()
80 {
81 m_cRef = 1;
82 m_dwAppDomainId = 0;
83 m_pExecutionContext = NULL;
84 m_pInspectionContext = NULL;
85 m_pFailureCache = NULL;
86 m_contextCS = NULL;
87 m_pTrustedPlatformAssemblyMap = nullptr;
88 m_pFileNameHash = nullptr;
89 }
90
91 ApplicationContext::~ApplicationContext()
92 {
93 SAFE_RELEASE(m_pExecutionContext);
94 SAFE_RELEASE(m_pInspectionContext);
95 SAFE_DELETE(m_pFailureCache);
96
97 if (m_contextCS != NULL)
98 {
99 ClrDeleteCriticalSection(m_contextCS);
100 }
101
102 if (m_pTrustedPlatformAssemblyMap != nullptr)
103 {
104 delete m_pTrustedPlatformAssemblyMap;
105 }
106
107 if (m_pFileNameHash != nullptr)
108 {
109 delete m_pFileNameHash;
110 }
111 }
112
113 HRESULT ApplicationContext::Init()
114 {
115 HRESULT hr = S_OK;
116 BINDER_LOG_ENTER(W("ApplicationContext::Init"));
117 BINDER_LOG_POINTER(W("this"), this);
118
119 ReleaseHolder<ExecutionContext> pExecutionContext;
120 ReleaseHolder<InspectionContext> pInspectionContext;
121
122 PropertyMap *pPropertyMap = NULL;
123 FailureCache *pFailureCache = NULL;
124
125 // Allocate context objects
126 SAFE_NEW(pExecutionContext, ExecutionContext);
127 SAFE_NEW(pInspectionContext, InspectionContext);
128
129 SAFE_NEW(pFailureCache, FailureCache);
130
131 m_contextCS = ClrCreateCriticalSection(
132 CrstFusionAppCtx,
133 CRST_REENTRANCY);
134 if (!m_contextCS)
135 {
136 SAFE_DELETE(pPropertyMap);
137 SAFE_DELETE(pFailureCache);
138 hr = E_OUTOFMEMORY;
139 }
140 else
141 {
142 m_pExecutionContext = pExecutionContext.Extract();
143 m_pInspectionContext = pInspectionContext.Extract();
144
145 m_pFailureCache = pFailureCache;
146 }
147
148 m_fCanExplicitlyBindToNativeImages = false;
149
150 Exit:
151 BINDER_LOG_LEAVE_HR(W("ApplicationContext::Init"), hr);
152 return hr;
153 }
154
155 HRESULT GetNextPath(SString& paths, SString::Iterator& startPos, SString& outPath)
156 {
157 HRESULT hr = S_OK;
158
159 bool wrappedWithQuotes = false;
160
161 // Skip any leading spaces or path separators
162 while (paths.Skip(startPos, W(' ')) || paths.Skip(startPos, PATH_SEPARATOR_CHAR_W)) {}
163
164 if (startPos == paths.End())
165 {
166 // No more paths in the string and we just skipped over some white space
167 outPath.Set(W(""));
168 return S_FALSE;
169 }
170
171 // Support paths being wrapped with quotations
172 if (paths.Skip(startPos, W('\"')))
173 {
174 wrappedWithQuotes = true;
175 }
176
177 SString::Iterator iEnd = startPos; // Where current path ends
178 SString::Iterator iNext; // Where next path starts
179 if (wrappedWithQuotes)
180 {
181 if (paths.Find(iEnd, W('\"')))
182 {
183 iNext = iEnd;
184 // Find where the next path starts - there should be a path separator right after the closing quotation mark
185 if (paths.Find(iNext, PATH_SEPARATOR_CHAR_W))
186 {
187 iNext++;
188 }
189 else
190 {
191 iNext = paths.End();
192 }
193 }
194 else
195 {
196 // There was no terminating quotation mark - that's bad
197 GO_WITH_HRESULT(E_INVALIDARG);
198 }
199 }
200 else if (paths.Find(iEnd, PATH_SEPARATOR_CHAR_W))
201 {
202 iNext = iEnd + 1;
203 }
204 else
205 {
206 iNext = iEnd = paths.End();
207 }
208
209 // Skip any trailing spaces
210 while (iEnd[-1] == W(' '))
211 {
212 iEnd--;
213 }
214
215 _ASSERTE(startPos < iEnd);
216
217 outPath.Set(paths, startPos, iEnd);
218 startPos = iNext;
219 Exit:
220 return hr;
221 }
222
223 HRESULT ApplicationContext::SetupBindingPaths(SString &sTrustedPlatformAssemblies,
224 SString &sPlatformResourceRoots,
225 SString &sAppPaths,
226 SString &sAppNiPaths,
227 BOOL fAcquireLock)
228 {
229 HRESULT hr = S_OK;
230 BINDER_LOG_ENTER(W("ApplicationContext::SetupBindingPaths"));
231 BINDER_LOG_POINTER(W("this"), this);
232
233#ifndef CROSSGEN_COMPILE
234 CRITSEC_Holder contextLock(fAcquireLock ? GetCriticalSectionCookie() : NULL);
235#endif
236 if (m_pTrustedPlatformAssemblyMap != nullptr)
237 {
238#if defined(BINDER_DEBUG_LOG)
239 BINDER_LOG(W("ApplicationContext::SetupBindingPaths: Binding paths already setup"));
240#endif // BINDER_LOG_STRING
241 GO_WITH_HRESULT(S_OK);
242 }
243
244
245 //
246 // Parse TrustedPlatformAssemblies
247 //
248 m_pTrustedPlatformAssemblyMap = new SimpleNameToFileNameMap();
249 m_pFileNameHash = new TpaFileNameHash();
250
251 sTrustedPlatformAssemblies.Normalize();
252
253 for (SString::Iterator i = sTrustedPlatformAssemblies.Begin(); i != sTrustedPlatformAssemblies.End(); )
254 {
255 SString fileName;
256 HRESULT pathResult = S_OK;
257 IF_FAIL_GO(pathResult = GetNextPath(sTrustedPlatformAssemblies, i, fileName));
258 if (pathResult == S_FALSE)
259 {
260 break;
261 }
262
263#ifndef CROSSGEN_COMPILE
264 if (Path::IsRelative(fileName))
265 {
266 BINDER_LOG_STRING(W("ApplicationContext::SetupBindingPaths: Relative path not allowed"), fileName);
267 GO_WITH_HRESULT(E_INVALIDARG);
268 }
269#endif
270
271 // Find the beginning of the simple name
272 SString::Iterator iSimpleNameStart = fileName.End();
273
274 if (!fileName.FindBack(iSimpleNameStart, DIRECTORY_SEPARATOR_CHAR_W))
275 {
276 iSimpleNameStart = fileName.Begin();
277 }
278 else
279 {
280 // Advance past the directory separator to the first character of the file name
281 iSimpleNameStart++;
282 }
283
284 if (iSimpleNameStart == fileName.End())
285 {
286 GO_WITH_HRESULT(E_INVALIDARG);
287 }
288
289 SString simpleName;
290 bool isNativeImage = false;
291
292 // GCC complains if we create SStrings inline as part of a function call
293 SString sNiDll(W(".ni.dll"));
294 SString sNiExe(W(".ni.exe"));
295 SString sNiWinmd(W(".ni.winmd"));
296 SString sDll(W(".dll"));
297 SString sExe(W(".exe"));
298 SString sWinmd(W(".winmd"));
299
300 if (fileName.EndsWithCaseInsensitive(sNiDll) ||
301 fileName.EndsWithCaseInsensitive(sNiExe))
302 {
303 simpleName.Set(fileName, iSimpleNameStart, fileName.End() - 7);
304 isNativeImage = true;
305 }
306 else if (fileName.EndsWithCaseInsensitive(sNiWinmd))
307 {
308 simpleName.Set(fileName, iSimpleNameStart, fileName.End() - 9);
309 isNativeImage = true;
310 }
311 else if (fileName.EndsWithCaseInsensitive(sDll) ||
312 fileName.EndsWithCaseInsensitive(sExe))
313 {
314 simpleName.Set(fileName, iSimpleNameStart, fileName.End() - 4);
315 }
316 else if (fileName.EndsWithCaseInsensitive(sWinmd))
317 {
318 simpleName.Set(fileName, iSimpleNameStart, fileName.End() - 6);
319 }
320 else
321 {
322 // Invalid filename
323 GO_WITH_HRESULT(E_INVALIDARG);
324 }
325
326 const SimpleNameToFileNameMapEntry *pExistingEntry = m_pTrustedPlatformAssemblyMap->LookupPtr(simpleName.GetUnicode());
327
328 if (pExistingEntry != nullptr)
329 {
330 //
331 // We want to store only the first entry matching a simple name we encounter.
332 // The exception is if we first store an IL reference and later in the string
333 // we encounter a native image. Since we don't touch IL in the presence of
334 // native images, we replace the IL entry with the NI.
335 //
336 if ((pExistingEntry->m_wszILFileName != nullptr && !isNativeImage) ||
337 (pExistingEntry->m_wszNIFileName != nullptr && isNativeImage))
338 {
339 BINDER_LOG_STRING(W("ApplicationContext::SetupBindingPaths: Skipping TPA entry because of already existing IL/NI entry for short name "), fileName.GetUnicode());
340 continue;
341 }
342 }
343
344 LPWSTR wszSimpleName = nullptr;
345 if (pExistingEntry == nullptr)
346 {
347 wszSimpleName = new WCHAR[simpleName.GetCount() + 1];
348 if (wszSimpleName == nullptr)
349 {
350 GO_WITH_HRESULT(E_OUTOFMEMORY);
351 }
352 wcscpy_s(wszSimpleName, simpleName.GetCount() + 1, simpleName.GetUnicode());
353 }
354 else
355 {
356 wszSimpleName = pExistingEntry->m_wszSimpleName;
357 }
358
359 LPWSTR wszFileName = new WCHAR[fileName.GetCount() + 1];
360 if (wszFileName == nullptr)
361 {
362 GO_WITH_HRESULT(E_OUTOFMEMORY);
363 }
364 wcscpy_s(wszFileName, fileName.GetCount() + 1, fileName.GetUnicode());
365
366 SimpleNameToFileNameMapEntry mapEntry;
367 mapEntry.m_wszSimpleName = wszSimpleName;
368 if (isNativeImage)
369 {
370 mapEntry.m_wszNIFileName = wszFileName;
371 mapEntry.m_wszILFileName = pExistingEntry == nullptr ? nullptr : pExistingEntry->m_wszILFileName;
372 }
373 else
374 {
375 mapEntry.m_wszILFileName = wszFileName;
376 mapEntry.m_wszNIFileName = pExistingEntry == nullptr ? nullptr : pExistingEntry->m_wszNIFileName;
377 }
378
379 m_pTrustedPlatformAssemblyMap->AddOrReplace(mapEntry);
380
381 FileNameMapEntry fileNameExistenceEntry;
382 fileNameExistenceEntry.m_wszFileName = wszFileName;
383 m_pFileNameHash->AddOrReplace(fileNameExistenceEntry);
384
385 BINDER_LOG_STRING(W("ApplicationContext::SetupBindingPaths: Added TPA entry"), wszFileName);
386 }
387
388 //
389 // Parse PlatformResourceRoots
390 //
391 sPlatformResourceRoots.Normalize();
392 for (SString::Iterator i = sPlatformResourceRoots.Begin(); i != sPlatformResourceRoots.End(); )
393 {
394 SString pathName;
395 HRESULT pathResult = S_OK;
396
397 IF_FAIL_GO(pathResult = GetNextPath(sPlatformResourceRoots, i, pathName));
398 if (pathResult == S_FALSE)
399 {
400 break;
401 }
402
403#ifndef CROSSGEN_COMPILE
404 if (Path::IsRelative(pathName))
405 {
406 BINDER_LOG_STRING(W("ApplicationContext::SetupBindingPaths: Relative path not allowed"), pathName);
407 GO_WITH_HRESULT(E_INVALIDARG);
408 }
409#endif
410
411 m_platformResourceRoots.Append(pathName);
412 BINDER_LOG_STRING(W("ApplicationContext::SetupBindingPaths: Added resource root"), pathName);
413 }
414
415 //
416 // Parse AppPaths
417 //
418 sAppPaths.Normalize();
419 for (SString::Iterator i = sAppPaths.Begin(); i != sAppPaths.End(); )
420 {
421 SString pathName;
422 HRESULT pathResult = S_OK;
423
424 IF_FAIL_GO(pathResult = GetNextPath(sAppPaths, i, pathName));
425 if (pathResult == S_FALSE)
426 {
427 break;
428 }
429
430#ifndef CROSSGEN_COMPILE
431 if (Path::IsRelative(pathName))
432 {
433 BINDER_LOG_STRING(W("ApplicationContext::SetupBindingPaths: Relative path not allowed"), pathName);
434 GO_WITH_HRESULT(E_INVALIDARG);
435 }
436#endif
437
438 m_appPaths.Append(pathName);
439 BINDER_LOG_STRING(W("ApplicationContext::SetupBindingPaths: Added App Path"), pathName);
440 }
441
442 //
443 // Parse AppNiPaths
444 //
445 sAppNiPaths.Normalize();
446 for (SString::Iterator i = sAppNiPaths.Begin(); i != sAppNiPaths.End(); )
447 {
448 SString pathName;
449 HRESULT pathResult = S_OK;
450
451 IF_FAIL_GO(pathResult = GetNextPath(sAppNiPaths, i, pathName));
452 if (pathResult == S_FALSE)
453 {
454 break;
455 }
456
457#ifndef CROSSGEN_COMPILE
458 if (Path::IsRelative(pathName))
459 {
460 BINDER_LOG_STRING(W("ApplicationContext::SetupBindingPaths: Relative path not allowed"), pathName);
461 GO_WITH_HRESULT(E_INVALIDARG);
462 }
463#endif
464
465 m_appNiPaths.Append(pathName);
466 BINDER_LOG_STRING(W("ApplicationContext::SetupBindingPaths: Added App NI Path"), pathName);
467 }
468
469 Exit:
470 BINDER_LOG_LEAVE_HR(W("ApplicationContext::SetupBindingPaths"), hr);
471 return hr;
472 }
473
474 HRESULT ApplicationContext::GetAssemblyIdentity(LPCSTR szTextualIdentity,
475 AssemblyIdentityUTF8 **ppAssemblyIdentity)
476 {
477 HRESULT hr = S_OK;
478 BINDER_LOG_ENTER(W("ApplicationContext::GetAssemblyIdentity"));
479 BINDER_LOG_POINTER(W("this"), this);
480
481 _ASSERTE(szTextualIdentity != NULL);
482 _ASSERTE(ppAssemblyIdentity != NULL);
483
484 CRITSEC_Holder contextLock(GetCriticalSectionCookie());
485
486 AssemblyIdentityUTF8 *pAssemblyIdentity = m_assemblyIdentityCache.Lookup(szTextualIdentity);
487 if (pAssemblyIdentity == NULL)
488 {
489 NewHolder<AssemblyIdentityUTF8> pNewAssemblyIdentity;
490 SString sTextualIdentity;
491
492 SAFE_NEW(pNewAssemblyIdentity, AssemblyIdentityUTF8);
493 sTextualIdentity.SetUTF8(szTextualIdentity);
494
495 IF_FAIL_GO(TextualIdentityParser::Parse(sTextualIdentity, pNewAssemblyIdentity));
496 IF_FAIL_GO(m_assemblyIdentityCache.Add(szTextualIdentity, pNewAssemblyIdentity));
497
498 pNewAssemblyIdentity->PopulateUTF8Fields();
499
500 pAssemblyIdentity = pNewAssemblyIdentity.Extract();
501 }
502
503 *ppAssemblyIdentity = pAssemblyIdentity;
504
505 Exit:
506 BINDER_LOG_LEAVE_HR(W("ApplicationContext::GetAssemblyIdentity"), hr);
507 return hr;
508 }
509
510 bool ApplicationContext::IsTpaListProvided()
511 {
512 return m_pTrustedPlatformAssemblyMap != nullptr;
513 }
514};
515