xref: /reactos/dll/win32/sxs/cache.c (revision 8a978a17)
1 /*
2  * IAssemblyCache implementation
3  *
4  * Copyright 2010 Hans Leidekker for CodeWeavers
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 <stdarg.h>
22 #ifdef __REACTOS__
23 #include <wchar.h>
24 #endif
25 
26 #define COBJMACROS
27 #define INITGUID
28 
29 #include "windef.h"
30 #include "winbase.h"
31 #include "ole2.h"
32 #include "winsxs.h"
33 #include "msxml2.h"
34 
35 #include "wine/debug.h"
36 #include "wine/list.h"
37 #include "sxs_private.h"
38 
39 WINE_DEFAULT_DEBUG_CHANNEL(sxs);
40 
41 static const WCHAR cache_mutex_nameW[] =
42     {'_','_','W','I','N','E','_','S','X','S','_','C','A','C','H','E','_','M','U','T','E','X','_','_',0};
43 
44 static const WCHAR win32W[] = {'w','i','n','3','2',0};
45 static const WCHAR win32_policyW[] = {'w','i','n','3','2','-','p','o','l','i','c','y',0};
46 static const WCHAR backslashW[] = {'\\',0};
47 
48 struct cache
49 {
50     IAssemblyCache IAssemblyCache_iface;
51     LONG refs;
52     HANDLE lock;
53 };
54 
55 static inline struct cache *impl_from_IAssemblyCache(IAssemblyCache *iface)
56 {
57     return CONTAINING_RECORD(iface, struct cache, IAssemblyCache_iface);
58 }
59 
60 static HRESULT WINAPI cache_QueryInterface(
61     IAssemblyCache *iface,
62     REFIID riid,
63     void **obj )
64 {
65     struct cache *cache = impl_from_IAssemblyCache(iface);
66 
67     TRACE("%p, %s, %p\n", cache, debugstr_guid(riid), obj);
68 
69     *obj = NULL;
70 
71     if (IsEqualIID(riid, &IID_IUnknown) ||
72         IsEqualIID(riid, &IID_IAssemblyCache))
73     {
74         IAssemblyCache_AddRef( iface );
75         *obj = cache;
76         return S_OK;
77     }
78 
79     return E_NOINTERFACE;
80 }
81 
82 static ULONG WINAPI cache_AddRef( IAssemblyCache *iface )
83 {
84     struct cache *cache = impl_from_IAssemblyCache(iface);
85     return InterlockedIncrement( &cache->refs );
86 }
87 
88 static ULONG WINAPI cache_Release( IAssemblyCache *iface )
89 {
90     struct cache *cache = impl_from_IAssemblyCache(iface);
91     ULONG refs = InterlockedDecrement( &cache->refs );
92 
93     if (!refs)
94     {
95         TRACE("destroying %p\n", cache);
96         CloseHandle( cache->lock );
97         HeapFree( GetProcessHeap(), 0, cache );
98     }
99     return refs;
100 }
101 
102 static unsigned int build_sxs_path( WCHAR *path )
103 {
104     static const WCHAR winsxsW[] = {'\\','w','i','n','s','x','s','\\',0};
105     unsigned int len = GetWindowsDirectoryW( path, MAX_PATH );
106 
107     memcpy( path + len, winsxsW, sizeof(winsxsW) );
108     return len + ARRAY_SIZE(winsxsW) - 1;
109 }
110 
111 static WCHAR *build_assembly_name( const WCHAR *arch, const WCHAR *name, const WCHAR *token,
112                                    const WCHAR *version, unsigned int *len )
113 {
114     static const WCHAR fmtW[] =
115         {'%','s','_','%','s','_','%','s','_','%','s','_','n','o','n','e','_','d','e','a','d','b','e','e','f',0};
116     unsigned int buflen = ARRAY_SIZE(fmtW);
117     WCHAR *ret;
118 
119     buflen += lstrlenW( arch );
120     buflen += lstrlenW( name );
121     buflen += lstrlenW( token );
122     buflen += lstrlenW( version );
123     if (!(ret = HeapAlloc( GetProcessHeap(), 0, buflen * sizeof(WCHAR) ))) return NULL;
124     *len = swprintf( ret, fmtW, arch, name, token, version );
125     return _wcslwr( ret );
126 }
127 
128 static WCHAR *build_manifest_path( const WCHAR *arch, const WCHAR *name, const WCHAR *token,
129                                    const WCHAR *version )
130 {
131     static const WCHAR fmtW[] =
132         {'%','s','m','a','n','i','f','e','s','t','s','\\','%','s','.','m','a','n','i','f','e','s','t',0};
133     WCHAR *path = NULL, *ret, sxsdir[MAX_PATH];
134     unsigned int len;
135 
136     if (!(path = build_assembly_name( arch, name, token, version, &len ))) return NULL;
137     len += ARRAY_SIZE(fmtW);
138     len += build_sxs_path( sxsdir );
139     if (!(ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
140     {
141         HeapFree( GetProcessHeap(), 0, path );
142         return NULL;
143     }
144     swprintf( ret, fmtW, sxsdir, path );
145     HeapFree( GetProcessHeap(), 0, path );
146     return ret;
147 }
148 
149 static WCHAR *build_policy_name( const WCHAR *arch, const WCHAR *name, const WCHAR *token,
150                                  unsigned int *len )
151 {
152     static const WCHAR fmtW[] =
153         {'%','s','_','%','s','_','%','s','_','n','o','n','e','_','d','e','a','d','b','e','e','f',0};
154     unsigned int buflen = ARRAY_SIZE(fmtW);
155     WCHAR *ret;
156 
157     buflen += lstrlenW( arch );
158     buflen += lstrlenW( name );
159     buflen += lstrlenW( token );
160     if (!(ret = HeapAlloc( GetProcessHeap(), 0, buflen * sizeof(WCHAR) ))) return NULL;
161     *len = swprintf( ret, fmtW, arch, name, token );
162     return _wcslwr( ret );
163 }
164 
165 static WCHAR *build_policy_path( const WCHAR *arch, const WCHAR *name, const WCHAR *token,
166                                  const WCHAR *version )
167 {
168     static const WCHAR fmtW[] =
169         {'%','s','p','o','l','i','c','i','e','s','\\','%','s','\\','%','s','.','p','o','l','i','c','y',0};
170     WCHAR *path = NULL, *ret, sxsdir[MAX_PATH];
171     unsigned int len;
172 
173     if (!(path = build_policy_name( arch, name, token, &len ))) return NULL;
174     len += ARRAY_SIZE(fmtW);
175     len += build_sxs_path( sxsdir );
176     len += lstrlenW( version );
177     if (!(ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
178     {
179         HeapFree( GetProcessHeap(), 0, path );
180         return NULL;
181     }
182     swprintf( ret, fmtW, sxsdir, path, version );
183     HeapFree( GetProcessHeap(), 0, path );
184     return ret;
185 }
186 
187 static void cache_lock( struct cache *cache )
188 {
189     WaitForSingleObject( cache->lock, INFINITE );
190 }
191 
192 static void cache_unlock( struct cache *cache )
193 {
194     ReleaseMutex( cache->lock );
195 }
196 
197 #define ASSEMBLYINFO_FLAG_INSTALLED 1
198 
199 static HRESULT WINAPI cache_QueryAssemblyInfo(
200     IAssemblyCache *iface,
201     DWORD flags,
202     LPCWSTR assembly_name,
203     ASSEMBLY_INFO *info )
204 {
205     struct cache *cache = impl_from_IAssemblyCache( iface );
206     IAssemblyName *name_obj;
207     const WCHAR *arch, *name, *token, *type, *version;
208     WCHAR *p, *path = NULL;
209     unsigned int len;
210     HRESULT hr;
211 
212     TRACE("%p, 0x%08x, %s, %p\n", iface, flags, debugstr_w(assembly_name), info);
213 
214     if (flags || (info && info->cbAssemblyInfo != sizeof(*info)))
215         return E_INVALIDARG;
216 
217     hr = CreateAssemblyNameObject( &name_obj, assembly_name, CANOF_PARSE_DISPLAY_NAME, 0 );
218     if (FAILED( hr ))
219         return hr;
220 
221     arch = get_name_attribute( name_obj, NAME_ATTR_ID_ARCH );
222     name = get_name_attribute( name_obj, NAME_ATTR_ID_NAME );
223     token = get_name_attribute( name_obj, NAME_ATTR_ID_TOKEN );
224     type = get_name_attribute( name_obj, NAME_ATTR_ID_TYPE );
225     version = get_name_attribute( name_obj, NAME_ATTR_ID_VERSION );
226     if (!arch || !name || !token || !type || !version)
227     {
228         IAssemblyName_Release( name_obj );
229         return HRESULT_FROM_WIN32( ERROR_SXS_MISSING_ASSEMBLY_IDENTITY_ATTRIBUTE );
230     }
231     if (!info)
232     {
233         IAssemblyName_Release( name_obj );
234         return S_OK;
235     }
236     cache_lock( cache );
237 
238     if (!wcscmp( type, win32W )) path = build_manifest_path( arch, name, token, version );
239     else if (!wcscmp( type, win32_policyW )) path = build_policy_path( arch, name, token, version );
240     else
241     {
242         hr = HRESULT_FROM_WIN32( ERROR_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE );
243         goto done;
244     }
245     if (!path)
246     {
247         hr = E_OUTOFMEMORY;
248         goto done;
249     }
250     hr = S_OK;
251     if (GetFileAttributesW( path ) != INVALID_FILE_ATTRIBUTES) /* FIXME: better check */
252     {
253         info->dwAssemblyFlags = ASSEMBLYINFO_FLAG_INSTALLED;
254         TRACE("assembly is installed\n");
255     }
256     if ((p = wcsrchr( path, '\\' ))) *p = 0;
257     len = lstrlenW( path ) + 1;
258     if (info->pszCurrentAssemblyPathBuf)
259     {
260         if (info->cchBuf < len)
261         {
262             info->cchBuf = len;
263             hr = HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER );
264         }
265         else lstrcpyW( info->pszCurrentAssemblyPathBuf, path );
266     }
267 
268 done:
269     HeapFree( GetProcessHeap(), 0, path );
270     IAssemblyName_Release( name_obj );
271     cache_unlock( cache );
272     return hr;
273 }
274 
275 static HRESULT WINAPI cache_CreateAssemblyCacheItem(
276     IAssemblyCache *iface,
277     DWORD flags,
278     PVOID reserved,
279     IAssemblyCacheItem **item,
280     LPCWSTR name )
281 {
282     FIXME("%p, 0x%08x, %p, %p, %s\n", iface, flags, reserved, item, debugstr_w(name));
283     return E_NOTIMPL;
284 }
285 
286 static HRESULT WINAPI cache_Reserved(
287     IAssemblyCache *iface,
288     IUnknown **reserved)
289 {
290     FIXME("%p\n", reserved);
291     return E_NOTIMPL;
292 }
293 
294 static BSTR get_attribute_value( IXMLDOMNamedNodeMap *map, const WCHAR *value_name )
295 {
296     HRESULT hr;
297     IXMLDOMNode *attr;
298     VARIANT var;
299     BSTR str;
300 
301     str = SysAllocString( value_name );
302     hr = IXMLDOMNamedNodeMap_getNamedItem( map, str, &attr );
303     SysFreeString( str );
304     if (hr != S_OK) return NULL;
305 
306     hr = IXMLDOMNode_get_nodeValue( attr, &var );
307     IXMLDOMNode_Release( attr );
308     if (hr != S_OK) return NULL;
309     if (V_VT(&var) != VT_BSTR)
310     {
311         VariantClear( &var );
312         return NULL;
313     }
314     TRACE("%s=%s\n", debugstr_w(value_name), debugstr_w(V_BSTR( &var )));
315     return V_BSTR( &var );
316 }
317 
318 struct file
319 {
320     struct list entry;
321     BSTR name;
322 };
323 
324 struct assembly
325 {
326     BSTR type;
327     BSTR name;
328     BSTR version;
329     BSTR arch;
330     BSTR token;
331     struct list files;
332 };
333 
334 static void free_assembly( struct assembly *assembly )
335 {
336     struct list *item, *cursor;
337 
338     if (!assembly) return;
339     SysFreeString( assembly->type );
340     SysFreeString( assembly->name );
341     SysFreeString( assembly->version );
342     SysFreeString( assembly->arch );
343     SysFreeString( assembly->token );
344     LIST_FOR_EACH_SAFE( item, cursor, &assembly->files )
345     {
346         struct file *file = LIST_ENTRY( item, struct file, entry );
347         list_remove( &file->entry );
348         SysFreeString( file->name );
349         HeapFree( GetProcessHeap(), 0, file );
350     }
351     HeapFree( GetProcessHeap(), 0, assembly );
352 }
353 
354 static HRESULT parse_files( IXMLDOMDocument *doc, struct assembly *assembly )
355 {
356     static const WCHAR fileW[] = {'f','i','l','e',0};
357     static const WCHAR nameW[] = {'n','a','m','e',0};
358     IXMLDOMNamedNodeMap *attrs;
359     IXMLDOMNodeList *list;
360     IXMLDOMNode *node;
361     struct file *f;
362     BSTR str;
363     HRESULT hr;
364     LONG len;
365 
366     str = SysAllocString( fileW );
367     hr = IXMLDOMDocument_getElementsByTagName( doc, str, &list );
368     SysFreeString( str );
369     if (hr != S_OK) return hr;
370 
371     hr = IXMLDOMNodeList_get_length( list, &len );
372     if (hr != S_OK) goto done;
373     TRACE("found %d files\n", len);
374     if (!len)
375     {
376         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
377         goto done;
378     }
379 
380     for (;;)
381     {
382         hr = IXMLDOMNodeList_nextNode( list, &node );
383         if (hr != S_OK || !node)
384         {
385             hr = S_OK;
386             break;
387         }
388 
389         /* FIXME: validate node type */
390 
391         hr = IXMLDOMNode_get_attributes( node, &attrs );
392         IXMLDOMNode_Release( node );
393         if (hr != S_OK)
394             goto done;
395 
396         if (!(f = HeapAlloc( GetProcessHeap(), 0, sizeof(struct file) )))
397         {
398             IXMLDOMNamedNodeMap_Release( attrs );
399             hr = E_OUTOFMEMORY;
400             goto done;
401         }
402 
403         f->name = get_attribute_value( attrs, nameW );
404         IXMLDOMNamedNodeMap_Release( attrs );
405         if (!f->name)
406         {
407             HeapFree( GetProcessHeap(), 0, f );
408             hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
409             goto done;
410         }
411         list_add_tail( &assembly->files, &f->entry );
412     }
413 
414     if (list_empty( &assembly->files ))
415     {
416         WARN("no files found\n");
417         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
418     }
419 
420 done:
421     IXMLDOMNodeList_Release( list );
422     return hr;
423 }
424 
425 static HRESULT parse_assembly( IXMLDOMDocument *doc, struct assembly **assembly )
426 {
427     static const WCHAR identityW[] = {'a','s','s','e','m','b','l','y','I','d','e','n','t','i','t','y',0};
428     static const WCHAR typeW[] = {'t','y','p','e',0};
429     static const WCHAR nameW[] = {'n','a','m','e',0};
430     static const WCHAR versionW[] = {'v','e','r','s','i','o','n',0};
431     static const WCHAR architectureW[] = {'p','r','o','c','e','s','s','o','r','A','r','c','h','i','t','e','c','t','u','r','e',0};
432     static const WCHAR tokenW[] = {'p','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
433     IXMLDOMNodeList *list = NULL;
434     IXMLDOMNode *node = NULL;
435     IXMLDOMNamedNodeMap *attrs = NULL;
436     struct assembly *a = NULL;
437     BSTR str;
438     HRESULT hr;
439     LONG len;
440 
441     str = SysAllocString( identityW );
442     hr = IXMLDOMDocument_getElementsByTagName( doc, str, &list );
443     SysFreeString( str );
444     if (hr != S_OK) goto done;
445 
446     hr = IXMLDOMNodeList_get_length( list, &len );
447     if (hr != S_OK) goto done;
448     if (!len)
449     {
450         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
451         goto done;
452     }
453     hr = IXMLDOMNodeList_nextNode( list, &node );
454     if (hr != S_OK) goto done;
455     if (!node)
456     {
457         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
458         goto done;
459     }
460     if (!(a = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct assembly) )))
461     {
462         hr = E_OUTOFMEMORY;
463         goto done;
464     }
465     list_init( &a->files );
466 
467     hr = IXMLDOMNode_get_attributes( node, &attrs );
468     if (hr != S_OK) goto done;
469 
470     a->type    = get_attribute_value( attrs, typeW );
471     a->name    = get_attribute_value( attrs, nameW );
472     a->version = get_attribute_value( attrs, versionW );
473     a->arch    = get_attribute_value( attrs, architectureW );
474     a->token   = get_attribute_value( attrs, tokenW );
475 
476     if (!a->type || (wcscmp( a->type, win32W ) && wcscmp( a->type, win32_policyW )) ||
477         !a->name || !a->version || !a->arch || !a->token)
478     {
479         WARN("invalid win32 assembly\n");
480         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
481         goto done;
482     }
483     if (!wcscmp( a->type, win32W )) hr = parse_files( doc, a );
484 
485 done:
486     if (attrs) IXMLDOMNamedNodeMap_Release( attrs );
487     if (node) IXMLDOMNode_Release( node );
488     if (list) IXMLDOMNodeList_Release( list );
489     if (hr == S_OK) *assembly = a;
490     else free_assembly( a );
491     return hr;
492 }
493 
494 static WCHAR *build_policy_filename( const WCHAR *arch, const WCHAR *name, const WCHAR *token,
495                                      const WCHAR *version )
496 {
497     static const WCHAR policiesW[] = {'p','o','l','i','c','i','e','s','\\',0};
498     static const WCHAR suffixW[] = {'.','p','o','l','i','c','y',0};
499     WCHAR sxsdir[MAX_PATH], *ret, *fullname;
500     unsigned int len;
501 
502     if (!(fullname = build_policy_name( arch, name, token, &len ))) return NULL;
503     len += build_sxs_path( sxsdir );
504     len += ARRAY_SIZE(policiesW) - 1;
505     len += lstrlenW( version );
506     len += ARRAY_SIZE(suffixW) - 1;
507     if (!(ret = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) )))
508     {
509         HeapFree( GetProcessHeap(), 0, fullname );
510         return NULL;
511     }
512     lstrcpyW( ret, sxsdir );
513     lstrcatW( ret, policiesW );
514     CreateDirectoryW( ret, NULL );
515     lstrcatW( ret, name );
516     CreateDirectoryW( ret, NULL );
517     lstrcatW( ret, backslashW );
518     lstrcatW( ret, version );
519     lstrcatW( ret, suffixW );
520 
521     HeapFree( GetProcessHeap(), 0, fullname );
522     return ret;
523 }
524 
525 static HRESULT install_policy( const WCHAR *manifest, struct assembly *assembly )
526 {
527     WCHAR *dst;
528     BOOL ret;
529 
530     /* FIXME: handle catalog file */
531 
532     dst = build_policy_filename( assembly->arch, assembly->name, assembly->token, assembly->version );
533     if (!dst) return E_OUTOFMEMORY;
534 
535     ret = CopyFileW( manifest, dst, FALSE );
536     HeapFree( GetProcessHeap(), 0, dst );
537     if (!ret)
538     {
539         HRESULT hr = HRESULT_FROM_WIN32( GetLastError() );
540         WARN("failed to copy policy manifest file 0x%08x\n", hr);
541         return hr;
542     }
543     return S_OK;
544 }
545 
546 static WCHAR *build_source_filename( const WCHAR *manifest, struct file *file )
547 {
548     WCHAR *src;
549     const WCHAR *p;
550     int len;
551 
552     p = wcsrchr( manifest, '\\' );
553     if (!p) p = wcsrchr( manifest, '/' );
554     if (!p) return strdupW( manifest );
555 
556     len = p - manifest + 1;
557     if (!(src = HeapAlloc( GetProcessHeap(), 0, (len + lstrlenW( file->name ) + 1) * sizeof(WCHAR) )))
558         return NULL;
559 
560     memcpy( src, manifest, len * sizeof(WCHAR) );
561     lstrcpyW( src + len, file->name );
562     return src;
563 }
564 
565 static WCHAR *build_manifest_filename( const WCHAR *arch, const WCHAR *name, const WCHAR *token,
566                                        const WCHAR *version )
567 {
568     static const WCHAR manifestsW[] = {'m','a','n','i','f','e','s','t','s','\\',0};
569     static const WCHAR suffixW[] = {'.','m','a','n','i','f','e','s','t',0};
570     WCHAR sxsdir[MAX_PATH], *ret, *fullname;
571     unsigned int len;
572 
573     if (!(fullname = build_assembly_name( arch, name, token, version, &len ))) return NULL;
574     len += build_sxs_path( sxsdir );
575     len += ARRAY_SIZE(manifestsW) - 1;
576     len += ARRAY_SIZE(suffixW) - 1;
577     if (!(ret = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) )))
578     {
579         HeapFree( GetProcessHeap(), 0, fullname );
580         return NULL;
581     }
582     lstrcpyW( ret, sxsdir );
583     lstrcatW( ret, manifestsW );
584     lstrcatW( ret, fullname );
585     lstrcatW( ret, suffixW );
586 
587     HeapFree( GetProcessHeap(), 0, fullname );
588     return ret;
589 }
590 
591 static HRESULT load_manifest( IXMLDOMDocument *doc, const WCHAR *filename )
592 {
593     HRESULT hr;
594     VARIANT var;
595     VARIANT_BOOL b;
596     BSTR str;
597 
598     str = SysAllocString( filename );
599     VariantInit( &var );
600     V_VT( &var ) = VT_BSTR;
601     V_BSTR( &var ) = str;
602     hr = IXMLDOMDocument_load( doc, var, &b );
603     SysFreeString( str );
604     if (hr != S_OK) return hr;
605     if (!b)
606     {
607         WARN("failed to load manifest\n");
608         return S_FALSE;
609     }
610     return S_OK;
611 }
612 
613 static HRESULT install_assembly( const WCHAR *manifest, struct assembly *assembly )
614 {
615     WCHAR sxsdir[MAX_PATH], *p, *name, *dst, *src;
616     unsigned int len, len_name, len_sxsdir = build_sxs_path( sxsdir );
617     struct file *file;
618     HRESULT hr = E_OUTOFMEMORY;
619     BOOL ret;
620 
621     dst = build_manifest_filename( assembly->arch, assembly->name, assembly->token, assembly->version );
622     if (!dst) return E_OUTOFMEMORY;
623 
624     ret = CopyFileW( manifest, dst, FALSE );
625     HeapFree( GetProcessHeap(), 0, dst );
626     if (!ret)
627     {
628         hr = HRESULT_FROM_WIN32( GetLastError() );
629         WARN("failed to copy manifest file 0x%08x\n", hr);
630         return hr;
631     }
632 
633     name = build_assembly_name( assembly->arch, assembly->name, assembly->token, assembly->version,
634                                 &len_name );
635     if (!name) return E_OUTOFMEMORY;
636 
637     /* FIXME: this should be a transaction */
638     LIST_FOR_EACH_ENTRY( file, &assembly->files, struct file, entry )
639     {
640         if (!(src = build_source_filename( manifest, file ))) goto done;
641 
642         len = len_sxsdir + len_name + lstrlenW( file->name );
643         if (!(dst = HeapAlloc( GetProcessHeap(), 0, (len + 2) * sizeof(WCHAR) )))
644         {
645             HeapFree( GetProcessHeap(), 0, src );
646             goto done;
647         }
648         lstrcpyW( dst, sxsdir );
649         lstrcatW( dst, name );
650         CreateDirectoryW( dst, NULL );
651 
652         lstrcatW( dst, backslashW );
653         lstrcatW( dst, file->name );
654         for (p = dst; *p; p++) *p = towlower( *p );
655 
656         ret = CopyFileW( src, dst, FALSE );
657         HeapFree( GetProcessHeap(), 0, src );
658         HeapFree( GetProcessHeap(), 0, dst );
659         if (!ret)
660         {
661             hr = HRESULT_FROM_WIN32( GetLastError() );
662             WARN("failed to copy file 0x%08x\n", hr);
663             goto done;
664         }
665     }
666     hr = S_OK;
667 
668 done:
669     HeapFree( GetProcessHeap(), 0, name );
670     return hr;
671 }
672 
673 static HRESULT WINAPI cache_InstallAssembly(
674     IAssemblyCache *iface,
675     DWORD flags,
676     LPCWSTR path,
677     LPCFUSION_INSTALL_REFERENCE ref )
678 {
679     struct cache *cache = impl_from_IAssemblyCache( iface );
680     HRESULT hr, init;
681     IXMLDOMDocument *doc = NULL;
682     struct assembly *assembly = NULL;
683 
684     TRACE("%p, 0x%08x, %s, %p\n", iface, flags, debugstr_w(path), ref);
685 
686     cache_lock( cache );
687     init = CoInitialize( NULL );
688 
689     hr = CoCreateInstance( &CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void **)&doc );
690     if (hr != S_OK)
691         goto done;
692 
693     if ((hr = load_manifest( doc, path )) != S_OK) goto done;
694     if ((hr = parse_assembly( doc, &assembly )) != S_OK) goto done;
695 
696     /* FIXME: verify name attributes */
697 
698     if (!wcscmp( assembly->type, win32_policyW ))
699         hr = install_policy( path, assembly );
700     else
701         hr = install_assembly( path, assembly );
702 
703 done:
704     free_assembly( assembly );
705     if (doc) IXMLDOMDocument_Release( doc );
706     if (SUCCEEDED(init)) CoUninitialize();
707     cache_unlock( cache );
708     return hr;
709 }
710 
711 static HRESULT uninstall_assembly( struct assembly *assembly )
712 {
713     WCHAR sxsdir[MAX_PATH], *name, *dirname, *filename;
714     unsigned int len, len_name, len_sxsdir = build_sxs_path( sxsdir );
715     HRESULT hr = E_OUTOFMEMORY;
716     struct file *file;
717 
718     name = build_assembly_name( assembly->arch, assembly->name, assembly->token, assembly->version,
719                                 &len_name );
720     if (!name) return E_OUTOFMEMORY;
721     if (!(dirname = HeapAlloc( GetProcessHeap(), 0, (len_sxsdir + len_name + 1) * sizeof(WCHAR) )))
722         goto done;
723     lstrcpyW( dirname, sxsdir );
724     lstrcpyW( dirname + len_sxsdir, name );
725 
726     LIST_FOR_EACH_ENTRY( file, &assembly->files, struct file, entry )
727     {
728         len = len_sxsdir + len_name + 1 + lstrlenW( file->name );
729         if (!(filename = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) goto done;
730         lstrcpyW( filename, dirname );
731         lstrcatW( filename, backslashW );
732         lstrcatW( filename, file->name );
733 
734         if (!DeleteFileW( filename )) WARN( "failed to delete file %u\n", GetLastError() );
735         HeapFree( GetProcessHeap(), 0, filename );
736     }
737     RemoveDirectoryW( dirname );
738     hr = S_OK;
739 
740 done:
741     HeapFree( GetProcessHeap(), 0, dirname );
742     HeapFree( GetProcessHeap(), 0, name );
743     return hr;
744 }
745 
746 static HRESULT WINAPI cache_UninstallAssembly(
747     IAssemblyCache *iface,
748     DWORD flags,
749     LPCWSTR assembly_name,
750     LPCFUSION_INSTALL_REFERENCE ref,
751     ULONG *disp )
752 {
753     struct cache *cache = impl_from_IAssemblyCache( iface );
754     HRESULT hr, init;
755     IXMLDOMDocument *doc = NULL;
756     struct assembly *assembly = NULL;
757     IAssemblyName *name_obj = NULL;
758     const WCHAR *arch, *name, *token, *type, *version;
759     WCHAR *p, *path = NULL;
760 
761     TRACE("%p, 0x%08x, %s, %p, %p\n", iface, flags, debugstr_w(assembly_name), ref, disp);
762 
763     if (ref)
764     {
765         FIXME("application reference not supported\n");
766         return E_NOTIMPL;
767     }
768     cache_lock( cache );
769     init = CoInitialize( NULL );
770 
771     hr = CreateAssemblyNameObject( &name_obj, assembly_name, CANOF_PARSE_DISPLAY_NAME, NULL );
772     if (FAILED( hr ))
773         goto done;
774 
775     arch = get_name_attribute( name_obj, NAME_ATTR_ID_ARCH );
776     name = get_name_attribute( name_obj, NAME_ATTR_ID_NAME );
777     token = get_name_attribute( name_obj, NAME_ATTR_ID_TOKEN );
778     type = get_name_attribute( name_obj, NAME_ATTR_ID_TYPE );
779     version = get_name_attribute( name_obj, NAME_ATTR_ID_VERSION );
780     if (!arch || !name || !token || !type || !version)
781     {
782         hr = E_INVALIDARG;
783         goto done;
784     }
785     if (!wcscmp( type, win32W )) path = build_manifest_filename( arch, name, token, version );
786     else if (!wcscmp( type, win32_policyW )) path = build_policy_filename( arch, name, token, version );
787     else
788     {
789         hr = E_INVALIDARG;
790         goto done;
791     }
792 
793     hr = CoCreateInstance( &CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void **)&doc );
794     if (hr != S_OK)
795         goto done;
796 
797     if ((hr = load_manifest( doc, path )) != S_OK) goto done;
798     if ((hr = parse_assembly( doc, &assembly )) != S_OK) goto done;
799 
800     if (!DeleteFileW( path )) WARN( "unable to remove manifest file %u\n", GetLastError() );
801     else if ((p = wcsrchr( path, '\\' )))
802     {
803         *p = 0;
804         RemoveDirectoryW( path );
805     }
806     if (!wcscmp( assembly->type, win32W )) hr = uninstall_assembly( assembly );
807 
808 done:
809     if (name_obj) IAssemblyName_Release( name_obj );
810     HeapFree( GetProcessHeap(), 0, path );
811     free_assembly( assembly );
812     if (doc) IXMLDOMDocument_Release( doc );
813     if (SUCCEEDED(init)) CoUninitialize();
814     cache_unlock( cache );
815     return hr;
816 }
817 
818 static const IAssemblyCacheVtbl cache_vtbl =
819 {
820     cache_QueryInterface,
821     cache_AddRef,
822     cache_Release,
823     cache_UninstallAssembly,
824     cache_QueryAssemblyInfo,
825     cache_CreateAssemblyCacheItem,
826     cache_Reserved,
827     cache_InstallAssembly
828 };
829 
830 /******************************************************************
831  *  CreateAssemblyCache   (SXS.@)
832  */
833 HRESULT WINAPI CreateAssemblyCache( IAssemblyCache **obj, DWORD reserved )
834 {
835     struct cache *cache;
836 
837     TRACE("%p, %u\n", obj, reserved);
838 
839     if (!obj)
840         return E_INVALIDARG;
841 
842     *obj = NULL;
843 
844     cache = HeapAlloc( GetProcessHeap(), 0, sizeof(struct cache) );
845     if (!cache)
846         return E_OUTOFMEMORY;
847 
848     cache->IAssemblyCache_iface.lpVtbl = &cache_vtbl;
849     cache->refs = 1;
850     cache->lock = CreateMutexW( NULL, FALSE, cache_mutex_nameW );
851     if (!cache->lock)
852     {
853         HeapFree( GetProcessHeap(), 0, cache );
854         return HRESULT_FROM_WIN32( GetLastError() );
855     }
856     *obj = &cache->IAssemblyCache_iface;
857     return S_OK;
858 }
859