[LT2013]
[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 #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 };