xref: /reactos/dll/win32/fusion/asmcache.c (revision e1ef0787)
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