1 /* 2 * IAssemblyCache implementation 3 * 4 * Copyright 2008 James Hawkins 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #include "fusionpriv.h" 22 23 static const WCHAR cache_mutex_nameW[] = 24 {'_','_','W','I','N','E','_','F','U','S','I','O','N','_','C','A','C','H','E','_','M','U','T','E','X','_','_',0}; 25 26 static BOOL create_full_path(LPCWSTR path) 27 { 28 LPWSTR new_path; 29 BOOL ret = TRUE; 30 int len; 31 32 new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) * sizeof(WCHAR)); 33 if (!new_path) 34 return FALSE; 35 36 strcpyW(new_path, path); 37 38 while ((len = strlenW(new_path)) && new_path[len - 1] == '\\') 39 new_path[len - 1] = 0; 40 41 while (!CreateDirectoryW(new_path, NULL)) 42 { 43 LPWSTR slash; 44 DWORD last_error = GetLastError(); 45 46 if(last_error == ERROR_ALREADY_EXISTS) 47 break; 48 49 if(last_error != ERROR_PATH_NOT_FOUND) 50 { 51 ret = FALSE; 52 break; 53 } 54 55 if(!(slash = strrchrW(new_path, '\\'))) 56 { 57 ret = FALSE; 58 break; 59 } 60 61 len = slash - new_path; 62 new_path[len] = 0; 63 if(!create_full_path(new_path)) 64 { 65 ret = FALSE; 66 break; 67 } 68 69 new_path[len] = '\\'; 70 } 71 72 HeapFree(GetProcessHeap(), 0, new_path); 73 return ret; 74 } 75 76 static BOOL get_assembly_directory(LPWSTR dir, DWORD size, const char *version, PEKIND architecture) 77 { 78 static const WCHAR dotnet[] = {'\\','M','i','c','r','o','s','o','f','t','.','N','E','T','\\',0}; 79 static const WCHAR gac[] = {'\\','a','s','s','e','m','b','l','y','\\','G','A','C',0}; 80 static const WCHAR msil[] = {'_','M','S','I','L',0}; 81 static const WCHAR x86[] = {'_','3','2',0}; 82 static const WCHAR amd64[] = {'_','6','4',0}; 83 DWORD len = GetWindowsDirectoryW(dir, size); 84 85 if (!strcmp(version, "v4.0.30319")) 86 { 87 strcpyW(dir + len, dotnet); 88 len += sizeof(dotnet)/sizeof(WCHAR) -1; 89 strcpyW(dir + len, gac + 1); 90 len += sizeof(gac)/sizeof(WCHAR) - 2; 91 } 92 else 93 { 94 strcpyW(dir + len, gac); 95 len += sizeof(gac)/sizeof(WCHAR) - 1; 96 } 97 switch (architecture) 98 { 99 case peNone: 100 break; 101 102 case peMSIL: 103 strcpyW(dir + len, msil); 104 break; 105 106 case peI386: 107 strcpyW(dir + len, x86); 108 break; 109 110 case peAMD64: 111 strcpyW(dir + len, amd64); 112 break; 113 114 default: 115 WARN("unhandled architecture %u\n", architecture); 116 return FALSE; 117 } 118 return TRUE; 119 } 120 121 /* IAssemblyCache */ 122 123 typedef struct { 124 IAssemblyCache IAssemblyCache_iface; 125 126 LONG ref; 127 HANDLE lock; 128 } IAssemblyCacheImpl; 129 130 static inline IAssemblyCacheImpl *impl_from_IAssemblyCache(IAssemblyCache *iface) 131 { 132 return CONTAINING_RECORD(iface, IAssemblyCacheImpl, IAssemblyCache_iface); 133 } 134 135 static HRESULT WINAPI IAssemblyCacheImpl_QueryInterface(IAssemblyCache *iface, 136 REFIID riid, LPVOID *ppobj) 137 { 138 IAssemblyCacheImpl *This = impl_from_IAssemblyCache(iface); 139 140 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppobj); 141 142 *ppobj = NULL; 143 144 if (IsEqualIID(riid, &IID_IUnknown) || 145 IsEqualIID(riid, &IID_IAssemblyCache)) 146 { 147 IAssemblyCache_AddRef(iface); 148 *ppobj = This; 149 return S_OK; 150 } 151 152 WARN("(%p, %s, %p): not found\n", This, debugstr_guid(riid), ppobj); 153 return E_NOINTERFACE; 154 } 155 156 static ULONG WINAPI IAssemblyCacheImpl_AddRef(IAssemblyCache *iface) 157 { 158 IAssemblyCacheImpl *This = impl_from_IAssemblyCache(iface); 159 ULONG refCount = InterlockedIncrement(&This->ref); 160 161 TRACE("(%p)->(ref before = %u)\n", This, refCount - 1); 162 163 return refCount; 164 } 165 166 static ULONG WINAPI IAssemblyCacheImpl_Release(IAssemblyCache *iface) 167 { 168 IAssemblyCacheImpl *cache = impl_from_IAssemblyCache(iface); 169 ULONG refCount = InterlockedDecrement( &cache->ref ); 170 171 TRACE("(%p)->(ref before = %u)\n", cache, refCount + 1); 172 173 if (!refCount) 174 { 175 CloseHandle( cache->lock ); 176 HeapFree( GetProcessHeap(), 0, cache ); 177 } 178 return refCount; 179 } 180 181 static void cache_lock( IAssemblyCacheImpl *cache ) 182 { 183 WaitForSingleObject( cache->lock, INFINITE ); 184 } 185 186 static void cache_unlock( IAssemblyCacheImpl *cache ) 187 { 188 ReleaseMutex( cache->lock ); 189 } 190 191 static HRESULT WINAPI IAssemblyCacheImpl_UninstallAssembly(IAssemblyCache *iface, 192 DWORD dwFlags, 193 LPCWSTR pszAssemblyName, 194 LPCFUSION_INSTALL_REFERENCE pRefData, 195 ULONG *pulDisposition) 196 { 197 HRESULT hr; 198 IAssemblyCacheImpl *cache = impl_from_IAssemblyCache(iface); 199 IAssemblyName *asmname, *next = NULL; 200 IAssemblyEnum *asmenum = NULL; 201 WCHAR *p, *path = NULL; 202 ULONG disp; 203 DWORD len; 204 205 TRACE("(%p, 0%08x, %s, %p, %p)\n", iface, dwFlags, 206 debugstr_w(pszAssemblyName), pRefData, pulDisposition); 207 208 if (pRefData) 209 { 210 FIXME("application reference not supported\n"); 211 return E_NOTIMPL; 212 } 213 hr = CreateAssemblyNameObject( &asmname, pszAssemblyName, CANOF_PARSE_DISPLAY_NAME, NULL ); 214 if (FAILED( hr )) 215 return hr; 216 217 cache_lock( cache ); 218 219 hr = CreateAssemblyEnum( &asmenum, NULL, asmname, ASM_CACHE_GAC, NULL ); 220 if (FAILED( hr )) 221 goto done; 222 223 hr = IAssemblyEnum_GetNextAssembly( asmenum, NULL, &next, 0 ); 224 if (hr == S_FALSE) 225 { 226 if (pulDisposition) 227 *pulDisposition = IASSEMBLYCACHE_UNINSTALL_DISPOSITION_ALREADY_UNINSTALLED; 228 goto done; 229 } 230 hr = IAssemblyName_GetPath( next, NULL, &len ); 231 if (hr != HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER )) 232 goto done; 233 234 if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) 235 { 236 hr = E_OUTOFMEMORY; 237 goto done; 238 } 239 hr = IAssemblyName_GetPath( next, path, &len ); 240 if (FAILED( hr )) 241 goto done; 242 243 if (DeleteFileW( path )) 244 { 245 if ((p = strrchrW( path, '\\' ))) 246 { 247 *p = 0; 248 RemoveDirectoryW( path ); 249 if ((p = strrchrW( path, '\\' ))) 250 { 251 *p = 0; 252 RemoveDirectoryW( path ); 253 } 254 } 255 disp = IASSEMBLYCACHE_UNINSTALL_DISPOSITION_UNINSTALLED; 256 hr = S_OK; 257 } 258 else 259 { 260 disp = IASSEMBLYCACHE_UNINSTALL_DISPOSITION_ALREADY_UNINSTALLED; 261 hr = S_FALSE; 262 } 263 if (pulDisposition) *pulDisposition = disp; 264 265 done: 266 IAssemblyName_Release( asmname ); 267 if (next) IAssemblyName_Release( next ); 268 if (asmenum) IAssemblyEnum_Release( asmenum ); 269 HeapFree( GetProcessHeap(), 0, path ); 270 cache_unlock( cache ); 271 return hr; 272 } 273 274 static HRESULT WINAPI IAssemblyCacheImpl_QueryAssemblyInfo(IAssemblyCache *iface, 275 DWORD dwFlags, 276 LPCWSTR pszAssemblyName, 277 ASSEMBLY_INFO *pAsmInfo) 278 { 279 IAssemblyCacheImpl *cache = impl_from_IAssemblyCache(iface); 280 IAssemblyName *asmname, *next = NULL; 281 IAssemblyEnum *asmenum = NULL; 282 HRESULT hr; 283 284 TRACE("(%p, %d, %s, %p)\n", iface, dwFlags, 285 debugstr_w(pszAssemblyName), pAsmInfo); 286 287 if (pAsmInfo) 288 { 289 if (pAsmInfo->cbAssemblyInfo == 0) 290 pAsmInfo->cbAssemblyInfo = sizeof(ASSEMBLY_INFO); 291 else if (pAsmInfo->cbAssemblyInfo != sizeof(ASSEMBLY_INFO)) 292 return E_INVALIDARG; 293 } 294 295 hr = CreateAssemblyNameObject(&asmname, pszAssemblyName, 296 CANOF_PARSE_DISPLAY_NAME, NULL); 297 if (FAILED(hr)) 298 return hr; 299 300 cache_lock( cache ); 301 302 hr = CreateAssemblyEnum(&asmenum, NULL, asmname, ASM_CACHE_GAC, NULL); 303 if (FAILED(hr)) 304 goto done; 305 306 for (;;) 307 { 308 hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0); 309 if (hr != S_OK) 310 { 311 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 312 goto done; 313 } 314 hr = IAssemblyName_IsEqual(asmname, next, ASM_CMPF_IL_ALL); 315 if (hr == S_OK) break; 316 } 317 318 if (!pAsmInfo) 319 goto done; 320 321 hr = IAssemblyName_GetPath(next, pAsmInfo->pszCurrentAssemblyPathBuf, &pAsmInfo->cchBuf); 322 323 pAsmInfo->dwAssemblyFlags = ASSEMBLYINFO_FLAG_INSTALLED; 324 325 done: 326 IAssemblyName_Release(asmname); 327 if (next) IAssemblyName_Release(next); 328 if (asmenum) IAssemblyEnum_Release(asmenum); 329 cache_unlock( cache ); 330 return hr; 331 } 332 333 static HRESULT WINAPI IAssemblyCacheImpl_CreateAssemblyCacheItem(IAssemblyCache *iface, 334 DWORD dwFlags, 335 PVOID pvReserved, 336 IAssemblyCacheItem **ppAsmItem, 337 LPCWSTR pszAssemblyName) 338 { 339 FIXME("(%p, %d, %p, %p, %s) stub!\n", iface, dwFlags, pvReserved, 340 ppAsmItem, debugstr_w(pszAssemblyName)); 341 342 return E_NOTIMPL; 343 } 344 345 static HRESULT WINAPI IAssemblyCacheImpl_CreateAssemblyScavenger(IAssemblyCache *iface, 346 IUnknown **ppUnkReserved) 347 { 348 FIXME("(%p, %p) stub!\n", iface, ppUnkReserved); 349 return E_NOTIMPL; 350 } 351 352 static HRESULT copy_file( const WCHAR *src_dir, DWORD src_len, const WCHAR *dst_dir, DWORD dst_len, 353 const WCHAR *filename ) 354 { 355 WCHAR *src_file, *dst_file; 356 DWORD len = strlenW( filename ); 357 HRESULT hr = S_OK; 358 359 if (!(src_file = HeapAlloc( GetProcessHeap(), 0, (src_len + len + 1) * sizeof(WCHAR) ))) 360 return E_OUTOFMEMORY; 361 memcpy( src_file, src_dir, src_len * sizeof(WCHAR) ); 362 strcpyW( src_file + src_len, filename ); 363 364 if (!(dst_file = HeapAlloc( GetProcessHeap(), 0, (dst_len + len + 1) * sizeof(WCHAR) ))) 365 { 366 HeapFree( GetProcessHeap(), 0, src_file ); 367 return E_OUTOFMEMORY; 368 } 369 memcpy( dst_file, dst_dir, dst_len * sizeof(WCHAR) ); 370 strcpyW( dst_file + dst_len, filename ); 371 372 if (!CopyFileW( src_file, dst_file, FALSE )) hr = HRESULT_FROM_WIN32( GetLastError() ); 373 HeapFree( GetProcessHeap(), 0, src_file ); 374 HeapFree( GetProcessHeap(), 0, dst_file ); 375 return hr; 376 } 377 378 static HRESULT WINAPI IAssemblyCacheImpl_InstallAssembly(IAssemblyCache *iface, 379 DWORD dwFlags, 380 LPCWSTR pszManifestFilePath, 381 LPCFUSION_INSTALL_REFERENCE pRefData) 382 { 383 static const WCHAR format[] = 384 {'%','s','\\','%','s','\\','%','s','_','_','%','s','\\',0}; 385 static const WCHAR format_v40[] = 386 {'%','s','\\','%','s','\\','v','4','.','0','_','%','s','_','_','%','s','\\',0}; 387 static const WCHAR ext_exe[] = {'.','e','x','e',0}; 388 static const WCHAR ext_dll[] = {'.','d','l','l',0}; 389 IAssemblyCacheImpl *cache = impl_from_IAssemblyCache(iface); 390 ASSEMBLY *assembly; 391 const WCHAR *extension, *filename, *src_dir; 392 WCHAR *name = NULL, *token = NULL, *version = NULL, *asmpath = NULL; 393 WCHAR asmdir[MAX_PATH], *p, **external_files = NULL, *dst_dir = NULL; 394 PEKIND architecture; 395 char *clr_version; 396 DWORD i, count = 0, src_len, dst_len = sizeof(format_v40)/sizeof(format_v40[0]); 397 HRESULT hr; 398 399 TRACE("(%p, %d, %s, %p)\n", iface, dwFlags, 400 debugstr_w(pszManifestFilePath), pRefData); 401 402 if (!pszManifestFilePath || !*pszManifestFilePath) 403 return E_INVALIDARG; 404 405 if (!(extension = strrchrW(pszManifestFilePath, '.'))) 406 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME); 407 408 if (lstrcmpiW(extension, ext_exe) && lstrcmpiW(extension, ext_dll)) 409 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME); 410 411 if (GetFileAttributesW(pszManifestFilePath) == INVALID_FILE_ATTRIBUTES) 412 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 413 414 hr = assembly_create(&assembly, pszManifestFilePath); 415 if (FAILED(hr)) 416 { 417 hr = COR_E_ASSEMBLYEXPECTED; 418 goto done; 419 } 420 421 hr = assembly_get_name(assembly, &name); 422 if (FAILED(hr)) 423 goto done; 424 425 hr = assembly_get_pubkey_token(assembly, &token); 426 if (FAILED(hr)) 427 goto done; 428 429 hr = assembly_get_version(assembly, &version); 430 if (FAILED(hr)) 431 goto done; 432 433 hr = assembly_get_runtime_version(assembly, &clr_version); 434 if (FAILED(hr)) 435 goto done; 436 437 hr = assembly_get_external_files(assembly, &external_files, &count); 438 if (FAILED(hr)) 439 goto done; 440 441 cache_lock( cache ); 442 443 architecture = assembly_get_architecture(assembly); 444 get_assembly_directory(asmdir, MAX_PATH, clr_version, architecture); 445 446 dst_len += strlenW(asmdir) + strlenW(name) + strlenW(version) + strlenW(token); 447 if (!(dst_dir = HeapAlloc(GetProcessHeap(), 0, dst_len * sizeof(WCHAR)))) 448 { 449 hr = E_OUTOFMEMORY; 450 goto done; 451 } 452 if (!strcmp(clr_version, "v4.0.30319")) 453 dst_len = sprintfW(dst_dir, format_v40, asmdir, name, version, token); 454 else 455 dst_len = sprintfW(dst_dir, format, asmdir, name, version, token); 456 457 create_full_path(dst_dir); 458 459 hr = assembly_get_path(assembly, &asmpath); 460 if (FAILED(hr)) 461 goto done; 462 463 if ((p = strrchrW(asmpath, '\\'))) 464 { 465 filename = p + 1; 466 src_dir = asmpath; 467 src_len = filename - asmpath; 468 } 469 else 470 { 471 filename = asmpath; 472 src_dir = NULL; 473 src_len = 0; 474 } 475 hr = copy_file(src_dir, src_len, dst_dir, dst_len, filename); 476 if (FAILED(hr)) 477 goto done; 478 479 for (i = 0; i < count; i++) 480 { 481 hr = copy_file(src_dir, src_len, dst_dir, dst_len, external_files[i]); 482 if (FAILED(hr)) 483 break; 484 } 485 486 done: 487 HeapFree(GetProcessHeap(), 0, name); 488 HeapFree(GetProcessHeap(), 0, token); 489 HeapFree(GetProcessHeap(), 0, version); 490 HeapFree(GetProcessHeap(), 0, asmpath); 491 HeapFree(GetProcessHeap(), 0, dst_dir); 492 for (i = 0; i < count; i++) HeapFree(GetProcessHeap(), 0, external_files[i]); 493 HeapFree(GetProcessHeap(), 0, external_files); 494 assembly_release(assembly); 495 cache_unlock( cache ); 496 return hr; 497 } 498 499 static const IAssemblyCacheVtbl AssemblyCacheVtbl = { 500 IAssemblyCacheImpl_QueryInterface, 501 IAssemblyCacheImpl_AddRef, 502 IAssemblyCacheImpl_Release, 503 IAssemblyCacheImpl_UninstallAssembly, 504 IAssemblyCacheImpl_QueryAssemblyInfo, 505 IAssemblyCacheImpl_CreateAssemblyCacheItem, 506 IAssemblyCacheImpl_CreateAssemblyScavenger, 507 IAssemblyCacheImpl_InstallAssembly 508 }; 509 510 /****************************************************************** 511 * CreateAssemblyCache (FUSION.@) 512 */ 513 HRESULT WINAPI CreateAssemblyCache(IAssemblyCache **ppAsmCache, DWORD dwReserved) 514 { 515 IAssemblyCacheImpl *cache; 516 517 TRACE("(%p, %d)\n", ppAsmCache, dwReserved); 518 519 if (!ppAsmCache) 520 return E_INVALIDARG; 521 522 *ppAsmCache = NULL; 523 524 cache = HeapAlloc(GetProcessHeap(), 0, sizeof(IAssemblyCacheImpl)); 525 if (!cache) 526 return E_OUTOFMEMORY; 527 528 cache->IAssemblyCache_iface.lpVtbl = &AssemblyCacheVtbl; 529 cache->ref = 1; 530 cache->lock = CreateMutexW( NULL, FALSE, cache_mutex_nameW ); 531 if (!cache->lock) 532 { 533 HeapFree( GetProcessHeap(), 0, cache ); 534 return HRESULT_FROM_WIN32( GetLastError() ); 535 } 536 *ppAsmCache = &cache->IAssemblyCache_iface; 537 return S_OK; 538 } 539 540 /* IAssemblyCacheItem */ 541 542 typedef struct { 543 IAssemblyCacheItem IAssemblyCacheItem_iface; 544 545 LONG ref; 546 } IAssemblyCacheItemImpl; 547 548 static inline IAssemblyCacheItemImpl *impl_from_IAssemblyCacheItem(IAssemblyCacheItem *iface) 549 { 550 return CONTAINING_RECORD(iface, IAssemblyCacheItemImpl, IAssemblyCacheItem_iface); 551 } 552 553 static HRESULT WINAPI IAssemblyCacheItemImpl_QueryInterface(IAssemblyCacheItem *iface, 554 REFIID riid, LPVOID *ppobj) 555 { 556 IAssemblyCacheItemImpl *This = impl_from_IAssemblyCacheItem(iface); 557 558 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppobj); 559 560 *ppobj = NULL; 561 562 if (IsEqualIID(riid, &IID_IUnknown) || 563 IsEqualIID(riid, &IID_IAssemblyCacheItem)) 564 { 565 IAssemblyCacheItem_AddRef(iface); 566 *ppobj = This; 567 return S_OK; 568 } 569 570 WARN("(%p, %s, %p): not found\n", This, debugstr_guid(riid), ppobj); 571 return E_NOINTERFACE; 572 } 573 574 static ULONG WINAPI IAssemblyCacheItemImpl_AddRef(IAssemblyCacheItem *iface) 575 { 576 IAssemblyCacheItemImpl *This = impl_from_IAssemblyCacheItem(iface); 577 ULONG refCount = InterlockedIncrement(&This->ref); 578 579 TRACE("(%p)->(ref before = %u)\n", This, refCount - 1); 580 581 return refCount; 582 } 583 584 static ULONG WINAPI IAssemblyCacheItemImpl_Release(IAssemblyCacheItem *iface) 585 { 586 IAssemblyCacheItemImpl *This = impl_from_IAssemblyCacheItem(iface); 587 ULONG refCount = InterlockedDecrement(&This->ref); 588 589 TRACE("(%p)->(ref before = %u)\n", This, refCount + 1); 590 591 if (!refCount) 592 HeapFree(GetProcessHeap(), 0, This); 593 594 return refCount; 595 } 596 597 static HRESULT WINAPI IAssemblyCacheItemImpl_CreateStream(IAssemblyCacheItem *iface, 598 DWORD dwFlags, 599 LPCWSTR pszStreamName, 600 DWORD dwFormat, 601 DWORD dwFormatFlags, 602 IStream **ppIStream, 603 ULARGE_INTEGER *puliMaxSize) 604 { 605 FIXME("(%p, %d, %s, %d, %d, %p, %p) stub!\n", iface, dwFlags, 606 debugstr_w(pszStreamName), dwFormat, dwFormatFlags, ppIStream, puliMaxSize); 607 608 return E_NOTIMPL; 609 } 610 611 static HRESULT WINAPI IAssemblyCacheItemImpl_Commit(IAssemblyCacheItem *iface, 612 DWORD dwFlags, 613 ULONG *pulDisposition) 614 { 615 FIXME("(%p, %d, %p) stub!\n", iface, dwFlags, pulDisposition); 616 return E_NOTIMPL; 617 } 618 619 static HRESULT WINAPI IAssemblyCacheItemImpl_AbortItem(IAssemblyCacheItem *iface) 620 { 621 FIXME("(%p) stub!\n", iface); 622 return E_NOTIMPL; 623 } 624 625 static const IAssemblyCacheItemVtbl AssemblyCacheItemVtbl = { 626 IAssemblyCacheItemImpl_QueryInterface, 627 IAssemblyCacheItemImpl_AddRef, 628 IAssemblyCacheItemImpl_Release, 629 IAssemblyCacheItemImpl_CreateStream, 630 IAssemblyCacheItemImpl_Commit, 631 IAssemblyCacheItemImpl_AbortItem 632 }; 633