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