Sync with trunk r62754.
[reactos.git] / dll / win32 / fusion / asmcache.c
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 };