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 #define WIN32_NO_STATUS 22 #define _INC_WINDOWS 23 #define COM_NO_WINDOWS_H 24 25 #include <stdarg.h> 26 //#include <stdio.h> 27 28 #define COBJMACROS 29 30 #include <windef.h> 31 #include <winbase.h> 32 //#include "winuser.h" 33 //#include "winver.h" 34 //#include "wincrypt.h" 35 #include <winreg.h> 36 #include <shlwapi.h> 37 //#include "dbghelp.h" 38 //#include "ole2.h" 39 #include <fusion.h> 40 #include <corerror.h> 41 42 #include "fusionpriv.h" 43 #include <wine/debug.h> 44 #include <wine/unicode.h> 45 46 WINE_DEFAULT_DEBUG_CHANNEL(fusion); 47 48 static const WCHAR cache_mutex_nameW[] = 49 {'_','_','W','I','N','E','_','F','U','S','I','O','N','_','C','A','C','H','E','_','M','U','T','E','X','_','_',0}; 50 51 static BOOL create_full_path(LPCWSTR path) 52 { 53 LPWSTR new_path; 54 BOOL ret = TRUE; 55 int len; 56 57 new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) * sizeof(WCHAR)); 58 if (!new_path) 59 return FALSE; 60 61 strcpyW(new_path, path); 62 63 while ((len = strlenW(new_path)) && new_path[len - 1] == '\\') 64 new_path[len - 1] = 0; 65 66 while (!CreateDirectoryW(new_path, NULL)) 67 { 68 LPWSTR slash; 69 DWORD last_error = GetLastError(); 70 71 if(last_error == ERROR_ALREADY_EXISTS) 72 break; 73 74 if(last_error != ERROR_PATH_NOT_FOUND) 75 { 76 ret = FALSE; 77 break; 78 } 79 80 if(!(slash = strrchrW(new_path, '\\'))) 81 { 82 ret = FALSE; 83 break; 84 } 85 86 len = slash - new_path; 87 new_path[len] = 0; 88 if(!create_full_path(new_path)) 89 { 90 ret = FALSE; 91 break; 92 } 93 94 new_path[len] = '\\'; 95 } 96 97 HeapFree(GetProcessHeap(), 0, new_path); 98 return ret; 99 } 100 101 static BOOL get_assembly_directory(LPWSTR dir, DWORD size, BYTE architecture) 102 { 103 static const WCHAR gac[] = {'\\','a','s','s','e','m','b','l','y','\\','G','A','C',0}; 104 105 static const WCHAR msil[] = {'_','M','S','I','L',0}; 106 static const WCHAR x86[] = {'_','3','2',0}; 107 static const WCHAR amd64[] = {'_','6','4',0}; 108 109 GetWindowsDirectoryW(dir, size); 110 strcatW(dir, gac); 111 112 switch (architecture) 113 { 114 case peMSIL: 115 strcatW(dir, msil); 116 break; 117 118 case peI386: 119 strcatW(dir, x86); 120 break; 121 122 case peAMD64: 123 strcatW(dir, amd64); 124 break; 125 } 126 127 return TRUE; 128 } 129 130 /* IAssemblyCache */ 131 132 typedef struct { 133 IAssemblyCache IAssemblyCache_iface; 134 135 LONG ref; 136 HANDLE lock; 137 } IAssemblyCacheImpl; 138 139 static inline IAssemblyCacheImpl *impl_from_IAssemblyCache(IAssemblyCache *iface) 140 { 141 return CONTAINING_RECORD(iface, IAssemblyCacheImpl, IAssemblyCache_iface); 142 } 143 144 static HRESULT WINAPI IAssemblyCacheImpl_QueryInterface(IAssemblyCache *iface, 145 REFIID riid, LPVOID *ppobj) 146 { 147 IAssemblyCacheImpl *This = impl_from_IAssemblyCache(iface); 148 149 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppobj); 150 151 *ppobj = NULL; 152 153 if (IsEqualIID(riid, &IID_IUnknown) || 154 IsEqualIID(riid, &IID_IAssemblyCache)) 155 { 156 IUnknown_AddRef(iface); 157 *ppobj = This; 158 return S_OK; 159 } 160 161 WARN("(%p, %s, %p): not found\n", This, debugstr_guid(riid), ppobj); 162 return E_NOINTERFACE; 163 } 164 165 static ULONG WINAPI IAssemblyCacheImpl_AddRef(IAssemblyCache *iface) 166 { 167 IAssemblyCacheImpl *This = impl_from_IAssemblyCache(iface); 168 ULONG refCount = InterlockedIncrement(&This->ref); 169 170 TRACE("(%p)->(ref before = %u)\n", This, refCount - 1); 171 172 return refCount; 173 } 174 175 static ULONG WINAPI IAssemblyCacheImpl_Release(IAssemblyCache *iface) 176 { 177 IAssemblyCacheImpl *cache = impl_from_IAssemblyCache(iface); 178 ULONG refCount = InterlockedDecrement( &cache->ref ); 179 180 TRACE("(%p)->(ref before = %u)\n", cache, refCount + 1); 181 182 if (!refCount) 183 { 184 CloseHandle( cache->lock ); 185 HeapFree( GetProcessHeap(), 0, cache ); 186 } 187 return refCount; 188 } 189 190 static void cache_lock( IAssemblyCacheImpl *cache ) 191 { 192 WaitForSingleObject( cache->lock, INFINITE ); 193 } 194 195 static void cache_unlock( IAssemblyCacheImpl *cache ) 196 { 197 ReleaseMutex( cache->lock ); 198 } 199 200 static HRESULT WINAPI IAssemblyCacheImpl_UninstallAssembly(IAssemblyCache *iface, 201 DWORD dwFlags, 202 LPCWSTR pszAssemblyName, 203 LPCFUSION_INSTALL_REFERENCE pRefData, 204 ULONG *pulDisposition) 205 { 206 HRESULT hr; 207 IAssemblyCacheImpl *cache = impl_from_IAssemblyCache(iface); 208 IAssemblyName *asmname, *next = NULL; 209 IAssemblyEnum *asmenum = NULL; 210 WCHAR *p, *path = NULL; 211 ULONG disp; 212 DWORD len; 213 214 TRACE("(%p, 0%08x, %s, %p, %p)\n", iface, dwFlags, 215 debugstr_w(pszAssemblyName), pRefData, pulDisposition); 216 217 if (pRefData) 218 { 219 FIXME("application reference not supported\n"); 220 return E_NOTIMPL; 221 } 222 hr = CreateAssemblyNameObject( &asmname, pszAssemblyName, CANOF_PARSE_DISPLAY_NAME, NULL ); 223 if (FAILED( hr )) 224 return hr; 225 226 cache_lock( cache ); 227 228 hr = CreateAssemblyEnum( &asmenum, NULL, asmname, ASM_CACHE_GAC, NULL ); 229 if (FAILED( hr )) 230 goto done; 231 232 hr = IAssemblyEnum_GetNextAssembly( asmenum, NULL, &next, 0 ); 233 if (hr == S_FALSE) 234 { 235 if (pulDisposition) 236 *pulDisposition = IASSEMBLYCACHE_UNINSTALL_DISPOSITION_ALREADY_UNINSTALLED; 237 goto done; 238 } 239 hr = IAssemblyName_GetPath( next, NULL, &len ); 240 if (hr != HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER )) 241 goto done; 242 243 if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) 244 { 245 hr = E_OUTOFMEMORY; 246 goto done; 247 } 248 hr = IAssemblyName_GetPath( next, path, &len ); 249 if (FAILED( hr )) 250 goto done; 251 252 if (DeleteFileW( path )) 253 { 254 if ((p = strrchrW( path, '\\' ))) 255 { 256 *p = 0; 257 RemoveDirectoryW( path ); 258 if ((p = strrchrW( path, '\\' ))) 259 { 260 *p = 0; 261 RemoveDirectoryW( path ); 262 } 263 } 264 disp = IASSEMBLYCACHE_UNINSTALL_DISPOSITION_UNINSTALLED; 265 hr = S_OK; 266 } 267 else 268 { 269 disp = IASSEMBLYCACHE_UNINSTALL_DISPOSITION_ALREADY_UNINSTALLED; 270 hr = S_FALSE; 271 } 272 if (pulDisposition) *pulDisposition = disp; 273 274 done: 275 IAssemblyName_Release( asmname ); 276 if (next) IAssemblyName_Release( next ); 277 if (asmenum) IAssemblyEnum_Release( asmenum ); 278 HeapFree( GetProcessHeap(), 0, path ); 279 cache_unlock( cache ); 280 return hr; 281 } 282 283 static HRESULT WINAPI IAssemblyCacheImpl_QueryAssemblyInfo(IAssemblyCache *iface, 284 DWORD dwFlags, 285 LPCWSTR pszAssemblyName, 286 ASSEMBLY_INFO *pAsmInfo) 287 { 288 IAssemblyCacheImpl *cache = impl_from_IAssemblyCache(iface); 289 IAssemblyName *asmname, *next = NULL; 290 IAssemblyEnum *asmenum = NULL; 291 HRESULT hr; 292 293 TRACE("(%p, %d, %s, %p)\n", iface, dwFlags, 294 debugstr_w(pszAssemblyName), pAsmInfo); 295 296 if (pAsmInfo) 297 { 298 if (pAsmInfo->cbAssemblyInfo == 0) 299 pAsmInfo->cbAssemblyInfo = sizeof(ASSEMBLY_INFO); 300 else if (pAsmInfo->cbAssemblyInfo != sizeof(ASSEMBLY_INFO)) 301 return E_INVALIDARG; 302 } 303 304 hr = CreateAssemblyNameObject(&asmname, pszAssemblyName, 305 CANOF_PARSE_DISPLAY_NAME, NULL); 306 if (FAILED(hr)) 307 return hr; 308 309 cache_lock( cache ); 310 311 hr = CreateAssemblyEnum(&asmenum, NULL, asmname, ASM_CACHE_GAC, NULL); 312 if (FAILED(hr)) 313 goto done; 314 315 hr = IAssemblyEnum_GetNextAssembly(asmenum, NULL, &next, 0); 316 if (hr == S_FALSE) 317 { 318 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 319 goto done; 320 } 321 322 if (!pAsmInfo) 323 goto done; 324 325 hr = IAssemblyName_GetPath(next, pAsmInfo->pszCurrentAssemblyPathBuf, &pAsmInfo->cchBuf); 326 327 pAsmInfo->dwAssemblyFlags = ASSEMBLYINFO_FLAG_INSTALLED; 328 329 done: 330 IAssemblyName_Release(asmname); 331 if (next) IAssemblyName_Release(next); 332 if (asmenum) IAssemblyEnum_Release(asmenum); 333 cache_unlock( cache ); 334 return hr; 335 } 336 337 static HRESULT WINAPI IAssemblyCacheImpl_CreateAssemblyCacheItem(IAssemblyCache *iface, 338 DWORD dwFlags, 339 PVOID pvReserved, 340 IAssemblyCacheItem **ppAsmItem, 341 LPCWSTR pszAssemblyName) 342 { 343 FIXME("(%p, %d, %p, %p, %s) stub!\n", iface, dwFlags, pvReserved, 344 ppAsmItem, debugstr_w(pszAssemblyName)); 345 346 return E_NOTIMPL; 347 } 348 349 static HRESULT WINAPI IAssemblyCacheImpl_CreateAssemblyScavenger(IAssemblyCache *iface, 350 IUnknown **ppUnkReserved) 351 { 352 FIXME("(%p, %p) stub!\n", iface, ppUnkReserved); 353 return E_NOTIMPL; 354 } 355 356 static HRESULT WINAPI IAssemblyCacheImpl_InstallAssembly(IAssemblyCache *iface, 357 DWORD dwFlags, 358 LPCWSTR pszManifestFilePath, 359 LPCFUSION_INSTALL_REFERENCE pRefData) 360 { 361 static const WCHAR format[] = {'%','s','\\','%','s','\\','%','s','_','_','%','s','\\',0}; 362 static const WCHAR ext_exe[] = {'.','e','x','e',0}; 363 static const WCHAR ext_dll[] = {'.','d','l','l',0}; 364 IAssemblyCacheImpl *cache = impl_from_IAssemblyCache(iface); 365 ASSEMBLY *assembly; 366 LPWSTR filename; 367 LPWSTR name = NULL; 368 LPWSTR token = NULL; 369 LPWSTR version = NULL; 370 LPWSTR asmpath = NULL; 371 WCHAR path[MAX_PATH]; 372 WCHAR asmdir[MAX_PATH]; 373 LPWSTR ext; 374 HRESULT hr; 375 376 TRACE("(%p, %d, %s, %p)\n", iface, dwFlags, 377 debugstr_w(pszManifestFilePath), pRefData); 378 379 if (!pszManifestFilePath || !*pszManifestFilePath) 380 return E_INVALIDARG; 381 382 if (!(ext = strrchrW(pszManifestFilePath, '.'))) 383 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME); 384 385 if (lstrcmpiW(ext, ext_exe) && lstrcmpiW(ext, ext_dll)) 386 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME); 387 388 if (GetFileAttributesW(pszManifestFilePath) == INVALID_FILE_ATTRIBUTES) 389 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 390 391 hr = assembly_create(&assembly, pszManifestFilePath); 392 if (FAILED(hr)) 393 { 394 hr = COR_E_ASSEMBLYEXPECTED; 395 goto done; 396 } 397 398 hr = assembly_get_name(assembly, &name); 399 if (FAILED(hr)) 400 goto done; 401 402 hr = assembly_get_pubkey_token(assembly, &token); 403 if (FAILED(hr)) 404 goto done; 405 406 hr = assembly_get_version(assembly, &version); 407 if (FAILED(hr)) 408 goto done; 409 410 cache_lock( cache ); 411 412 get_assembly_directory(asmdir, MAX_PATH, assembly_get_architecture(assembly)); 413 414 sprintfW(path, format, asmdir, name, version, token); 415 416 create_full_path(path); 417 418 hr = assembly_get_path(assembly, &asmpath); 419 if (FAILED(hr)) 420 goto done; 421 422 filename = PathFindFileNameW(asmpath); 423 424 strcatW(path, filename); 425 if (!CopyFileW(asmpath, path, FALSE)) 426 hr = HRESULT_FROM_WIN32(GetLastError()); 427 428 done: 429 HeapFree(GetProcessHeap(), 0, name); 430 HeapFree(GetProcessHeap(), 0, token); 431 HeapFree(GetProcessHeap(), 0, version); 432 HeapFree(GetProcessHeap(), 0, asmpath); 433 assembly_release(assembly); 434 cache_unlock( cache ); 435 return hr; 436 } 437 438 static const IAssemblyCacheVtbl AssemblyCacheVtbl = { 439 IAssemblyCacheImpl_QueryInterface, 440 IAssemblyCacheImpl_AddRef, 441 IAssemblyCacheImpl_Release, 442 IAssemblyCacheImpl_UninstallAssembly, 443 IAssemblyCacheImpl_QueryAssemblyInfo, 444 IAssemblyCacheImpl_CreateAssemblyCacheItem, 445 IAssemblyCacheImpl_CreateAssemblyScavenger, 446 IAssemblyCacheImpl_InstallAssembly 447 }; 448 449 /****************************************************************** 450 * CreateAssemblyCache (FUSION.@) 451 */ 452 HRESULT WINAPI CreateAssemblyCache(IAssemblyCache **ppAsmCache, DWORD dwReserved) 453 { 454 IAssemblyCacheImpl *cache; 455 456 TRACE("(%p, %d)\n", ppAsmCache, dwReserved); 457 458 if (!ppAsmCache) 459 return E_INVALIDARG; 460 461 *ppAsmCache = NULL; 462 463 cache = HeapAlloc(GetProcessHeap(), 0, sizeof(IAssemblyCacheImpl)); 464 if (!cache) 465 return E_OUTOFMEMORY; 466 467 cache->IAssemblyCache_iface.lpVtbl = &AssemblyCacheVtbl; 468 cache->ref = 1; 469 cache->lock = CreateMutexW( NULL, FALSE, cache_mutex_nameW ); 470 if (!cache->lock) 471 { 472 HeapFree( GetProcessHeap(), 0, cache ); 473 return HRESULT_FROM_WIN32( GetLastError() ); 474 } 475 *ppAsmCache = &cache->IAssemblyCache_iface; 476 return S_OK; 477 } 478 479 /* IAssemblyCacheItem */ 480 481 typedef struct { 482 IAssemblyCacheItem IAssemblyCacheItem_iface; 483 484 LONG ref; 485 } IAssemblyCacheItemImpl; 486 487 static inline IAssemblyCacheItemImpl *impl_from_IAssemblyCacheItem(IAssemblyCacheItem *iface) 488 { 489 return CONTAINING_RECORD(iface, IAssemblyCacheItemImpl, IAssemblyCacheItem_iface); 490 } 491 492 static HRESULT WINAPI IAssemblyCacheItemImpl_QueryInterface(IAssemblyCacheItem *iface, 493 REFIID riid, LPVOID *ppobj) 494 { 495 IAssemblyCacheItemImpl *This = impl_from_IAssemblyCacheItem(iface); 496 497 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppobj); 498 499 *ppobj = NULL; 500 501 if (IsEqualIID(riid, &IID_IUnknown) || 502 IsEqualIID(riid, &IID_IAssemblyCacheItem)) 503 { 504 IUnknown_AddRef(iface); 505 *ppobj = This; 506 return S_OK; 507 } 508 509 WARN("(%p, %s, %p): not found\n", This, debugstr_guid(riid), ppobj); 510 return E_NOINTERFACE; 511 } 512 513 static ULONG WINAPI IAssemblyCacheItemImpl_AddRef(IAssemblyCacheItem *iface) 514 { 515 IAssemblyCacheItemImpl *This = impl_from_IAssemblyCacheItem(iface); 516 ULONG refCount = InterlockedIncrement(&This->ref); 517 518 TRACE("(%p)->(ref before = %u)\n", This, refCount - 1); 519 520 return refCount; 521 } 522 523 static ULONG WINAPI IAssemblyCacheItemImpl_Release(IAssemblyCacheItem *iface) 524 { 525 IAssemblyCacheItemImpl *This = impl_from_IAssemblyCacheItem(iface); 526 ULONG refCount = InterlockedDecrement(&This->ref); 527 528 TRACE("(%p)->(ref before = %u)\n", This, refCount + 1); 529 530 if (!refCount) 531 HeapFree(GetProcessHeap(), 0, This); 532 533 return refCount; 534 } 535 536 static HRESULT WINAPI IAssemblyCacheItemImpl_CreateStream(IAssemblyCacheItem *iface, 537 DWORD dwFlags, 538 LPCWSTR pszStreamName, 539 DWORD dwFormat, 540 DWORD dwFormatFlags, 541 IStream **ppIStream, 542 ULARGE_INTEGER *puliMaxSize) 543 { 544 FIXME("(%p, %d, %s, %d, %d, %p, %p) stub!\n", iface, dwFlags, 545 debugstr_w(pszStreamName), dwFormat, dwFormatFlags, ppIStream, puliMaxSize); 546 547 return E_NOTIMPL; 548 } 549 550 static HRESULT WINAPI IAssemblyCacheItemImpl_Commit(IAssemblyCacheItem *iface, 551 DWORD dwFlags, 552 ULONG *pulDisposition) 553 { 554 FIXME("(%p, %d, %p) stub!\n", iface, dwFlags, pulDisposition); 555 return E_NOTIMPL; 556 } 557 558 static HRESULT WINAPI IAssemblyCacheItemImpl_AbortItem(IAssemblyCacheItem *iface) 559 { 560 FIXME("(%p) stub!\n", iface); 561 return E_NOTIMPL; 562 } 563 564 static const IAssemblyCacheItemVtbl AssemblyCacheItemVtbl = { 565 IAssemblyCacheItemImpl_QueryInterface, 566 IAssemblyCacheItemImpl_AddRef, 567 IAssemblyCacheItemImpl_Release, 568 IAssemblyCacheItemImpl_CreateStream, 569 IAssemblyCacheItemImpl_Commit, 570 IAssemblyCacheItemImpl_AbortItem 571 }; 572