ntdll: Use the getdents64 system call on all Linux platforms.
[wine] / dlls / sxs / cache.c
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
23 #define COBJMACROS
24 #define INITGUID
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "ole2.h"
29 #include "winsxs.h"
30 #include "msxml2.h"
31
32 #include "wine/debug.h"
33 #include "wine/list.h"
34 #include "wine/unicode.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(sxs);
37
38 static inline WCHAR *strdupW( const WCHAR *s )
39 {
40     WCHAR *t;
41     if (!s) return NULL;
42     if ((t = HeapAlloc( GetProcessHeap(), 0, (strlenW( s ) + 1) * sizeof(WCHAR) ))) strcpyW( t, s );
43     return t;
44 }
45
46 struct cache
47 {
48     const IAssemblyCacheVtbl *vtbl;
49     LONG refs;
50 };
51
52 static HRESULT WINAPI cache_QueryInterface(
53     IAssemblyCache *iface,
54     REFIID riid,
55     void **obj )
56 {
57     struct cache *cache = (struct cache *)iface;
58
59     TRACE("%p, %s, %p\n", cache, debugstr_guid(riid), obj);
60
61     *obj = NULL;
62
63     if (IsEqualIID(riid, &IID_IUnknown) ||
64         IsEqualIID(riid, &IID_IAssemblyCache))
65     {
66         IUnknown_AddRef( iface );
67         *obj = cache;
68         return S_OK;
69     }
70
71     return E_NOINTERFACE;
72 }
73
74 static ULONG WINAPI cache_AddRef( IAssemblyCache *iface )
75 {
76     struct cache *cache = (struct cache *)iface;
77     return InterlockedIncrement( &cache->refs );
78 }
79
80 static ULONG WINAPI cache_Release( IAssemblyCache *iface )
81 {
82     struct cache *cache = (struct cache *)iface;
83     ULONG refs = InterlockedDecrement( &cache->refs );
84
85     if (!refs)
86     {
87         TRACE("destroying %p\n", cache);
88         HeapFree( GetProcessHeap(), 0, cache );
89     }
90     return refs;
91 }
92
93 static HRESULT WINAPI cache_UninstallAssembly(
94     IAssemblyCache *iface,
95     DWORD flags,
96     LPCWSTR name,
97     LPCFUSION_INSTALL_REFERENCE ref,
98     ULONG *disp )
99 {
100     FIXME("%p, 0x%08x, %s, %p, %p\n", iface, flags, debugstr_w(name), ref, disp);
101     return E_NOTIMPL;
102 }
103
104 static HRESULT WINAPI cache_QueryAssemblyInfo(
105     IAssemblyCache *iface,
106     DWORD flags,
107     LPCWSTR name,
108     ASSEMBLY_INFO *info )
109 {
110     FIXME("%p, 0x%08x, %s, %p\n", iface, flags, debugstr_w(name), info);
111     return E_NOTIMPL;
112 }
113
114 static HRESULT WINAPI cache_CreateAssemblyCacheItem(
115     IAssemblyCache *iface,
116     DWORD flags,
117     PVOID reserved,
118     IAssemblyCacheItem **item,
119     LPCWSTR name )
120 {
121     FIXME("%p, 0x%08x, %p, %p, %s\n", iface, flags, reserved, item, debugstr_w(name));
122     return E_NOTIMPL;
123 }
124
125 static HRESULT WINAPI cache_Reserved(
126     IAssemblyCache *iface,
127     IUnknown **reserved)
128 {
129     FIXME("%p\n", reserved);
130     return E_NOTIMPL;
131 }
132
133 static BSTR get_attribute_value( IXMLDOMNamedNodeMap *map, const WCHAR *value_name )
134 {
135     HRESULT hr;
136     IXMLDOMNode *attr;
137     VARIANT var;
138     BSTR str;
139
140     str = SysAllocString( value_name );
141     hr = IXMLDOMNamedNodeMap_getNamedItem( map, str, &attr );
142     SysFreeString( str );
143     if (hr != S_OK) return NULL;
144
145     hr = IXMLDOMNode_get_nodeValue( attr, &var );
146     IXMLDOMNode_Release( attr );
147     if (hr != S_OK) return NULL;
148     if (V_VT(&var) != VT_BSTR)
149     {
150         VariantClear( &var );
151         return NULL;
152     }
153     TRACE("%s=%s\n", debugstr_w(value_name), debugstr_w(V_BSTR( &var )));
154     return V_BSTR( &var );
155 }
156
157 struct file
158 {
159     struct list entry;
160     BSTR name;
161 };
162
163 struct assembly
164 {
165     BSTR type;
166     BSTR name;
167     BSTR version;
168     BSTR arch;
169     BSTR token;
170     struct list files;
171 };
172
173 static void free_assembly( struct assembly *assembly )
174 {
175     struct list *item, *cursor;
176
177     if (!assembly) return;
178     SysFreeString( assembly->type );
179     SysFreeString( assembly->name );
180     SysFreeString( assembly->version );
181     SysFreeString( assembly->arch );
182     SysFreeString( assembly->token );
183     LIST_FOR_EACH_SAFE( item, cursor, &assembly->files )
184     {
185         struct file *file = LIST_ENTRY( item, struct file, entry );
186         list_remove( &file->entry );
187         SysFreeString( file->name );
188         HeapFree( GetProcessHeap(), 0, file );
189     }
190     HeapFree( GetProcessHeap(), 0, assembly );
191 }
192
193 static HRESULT parse_files( IXMLDOMDocument *doc, struct assembly *assembly )
194 {
195     static const WCHAR fileW[] = {'f','i','l','e',0};
196     static const WCHAR nameW[] = {'n','a','m','e',0};
197     IXMLDOMNamedNodeMap *attrs;
198     IXMLDOMNodeList *list;
199     IXMLDOMNode *node;
200     struct file *f;
201     BSTR str;
202     HRESULT hr;
203     LONG len;
204
205     str = SysAllocString( fileW );
206     hr = IXMLDOMDocument_getElementsByTagName( doc, str, &list );
207     SysFreeString( str );
208     if (hr != S_OK) return hr;
209
210     hr = IXMLDOMNodeList_get_length( list, &len );
211     if (hr != S_OK) goto done;
212     TRACE("found %d files\n", len);
213     if (!len)
214     {
215         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
216         goto done;
217     }
218
219     for (;;)
220     {
221         hr = IXMLDOMNodeList_nextNode( list, &node );
222         if (hr != S_OK || !node)
223         {
224             hr = S_OK;
225             break;
226         }
227
228         /* FIXME: validate node type */
229
230         hr = IXMLDOMNode_get_attributes( node, &attrs );
231         IXMLDOMNode_Release( node );
232         if (hr != S_OK)
233             goto done;
234
235         if (!(f = HeapAlloc( GetProcessHeap(), 0, sizeof(struct file) )))
236         {
237             IXMLDOMNamedNodeMap_Release( attrs );
238             hr = E_OUTOFMEMORY;
239             goto done;
240         }
241
242         f->name = get_attribute_value( attrs, nameW );
243         IXMLDOMNamedNodeMap_Release( attrs );
244         if (!f->name)
245         {
246             HeapFree( GetProcessHeap(), 0, f );
247             hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
248             goto done;
249         }
250         list_add_tail( &assembly->files, &f->entry );
251     }
252
253     if (list_empty( &assembly->files ))
254     {
255         WARN("no files found\n");
256         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
257     }
258
259 done:
260     IXMLDOMNodeList_Release( list );
261     return hr;
262 }
263
264 static HRESULT parse_assembly( IXMLDOMDocument *doc, struct assembly **assembly )
265 {
266     static const WCHAR identityW[] = {'a','s','s','e','m','b','l','y','I','d','e','n','t','i','t','y',0};
267     static const WCHAR typeW[] = {'t','y','p','e',0};
268     static const WCHAR nameW[] = {'n','a','m','e',0};
269     static const WCHAR versionW[] = {'v','e','r','s','i','o','n',0};
270     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};
271     static const WCHAR tokenW[] = {'p','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
272     static const WCHAR win32W[] = {'w','i','n','3','2',0};
273     static const WCHAR policyW[] = {'w','i','n','3','2','-','p','o','l','i','c','y',0};
274     IXMLDOMNodeList *list = NULL;
275     IXMLDOMNode *node = NULL;
276     IXMLDOMNamedNodeMap *attrs = NULL;
277     struct assembly *a = NULL;
278     BSTR str;
279     HRESULT hr;
280     LONG len;
281
282     str = SysAllocString( identityW );
283     hr = IXMLDOMDocument_getElementsByTagName( doc, str, &list );
284     SysFreeString( str );
285     if (hr != S_OK) goto done;
286
287     hr = IXMLDOMNodeList_get_length( list, &len );
288     if (hr != S_OK) goto done;
289     if (!len)
290     {
291         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
292         goto done;
293     }
294     hr = IXMLDOMNodeList_nextNode( list, &node );
295     if (hr != S_OK) goto done;
296     if (!node)
297     {
298         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
299         goto done;
300     }
301     if (!(a = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct assembly) )))
302     {
303         hr = E_OUTOFMEMORY;
304         goto done;
305     }
306     list_init( &a->files );
307
308     hr = IXMLDOMNode_get_attributes( node, &attrs );
309     if (hr != S_OK) goto done;
310
311     a->type    = get_attribute_value( attrs, typeW );
312     a->name    = get_attribute_value( attrs, nameW );
313     a->version = get_attribute_value( attrs, versionW );
314     a->arch    = get_attribute_value( attrs, architectureW );
315     a->token   = get_attribute_value( attrs, tokenW );
316
317     if (!a->type || (strcmpW( a->type, win32W ) && strcmpW( a->type, policyW )) ||
318         !a->name || !a->version || !a->arch || !a->token)
319     {
320         WARN("invalid win32 assembly\n");
321         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
322         goto done;
323     }
324     if (!strcmpW( a->type, win32W )) hr = parse_files( doc, a );
325
326 done:
327     if (attrs) IXMLDOMNamedNodeMap_Release( attrs );
328     if (node) IXMLDOMNode_Release( node );
329     if (list) IXMLDOMNodeList_Release( list );
330     if (hr == S_OK) *assembly = a;
331     else free_assembly( a );
332     return hr;
333 }
334
335 static WCHAR *build_sxs_path( void )
336 {
337     static const WCHAR winsxsW[] = {'\\','w','i','n','s','x','s','\\',0};
338     WCHAR sxsdir[MAX_PATH];
339
340     GetWindowsDirectoryW( sxsdir, MAX_PATH );
341     strcatW( sxsdir, winsxsW );
342     return strdupW( sxsdir );
343 }
344
345 static WCHAR *build_assembly_name( struct assembly *assembly )
346 {
347     static const WCHAR fmtW[] =
348         {'%','s','_','%','s','_','%','s','_','%','s','_','n','o','n','e','_','d','e','a','d','b','e','e','f',0};
349     WCHAR *ret, *p;
350     int len;
351
352     len = strlenW( fmtW );
353     len += strlenW( assembly->arch );
354     len += strlenW( assembly->name );
355     len += strlenW( assembly->token );
356     len += strlenW( assembly->version );
357
358     if (!(ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
359     sprintfW( ret, fmtW, assembly->arch, assembly->name, assembly->token, assembly->version );
360     for (p = ret; *p; p++) *p = tolowerW( *p );
361     return ret;
362 }
363
364 static WCHAR *build_policy_name( struct assembly *assembly )
365 {
366     static const WCHAR fmtW[] =
367         {'%','s','_','%','s','_','%','s','_','n','o','n','e','_','d','e','a','d','b','e','e','f',0};
368     WCHAR *ret, *p;
369     int len;
370
371     len = strlenW( fmtW );
372     len += strlenW( assembly->arch );
373     len += strlenW( assembly->name );
374     len += strlenW( assembly->token );
375
376     if (!(ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
377     sprintfW( ret, fmtW, assembly->arch, assembly->name, assembly->token );
378     for (p = ret; *p; p++) *p = tolowerW( *p );
379     return ret;
380 }
381
382 static HRESULT install_policy( const WCHAR *manifest, struct assembly *assembly )
383 {
384     static const WCHAR policiesW[] = {'p','o','l','i','c','i','e','s','\\',0};
385     static const WCHAR suffixW[] = {'.','p','o','l','i','c','y',0};
386     static const WCHAR backslashW[] = {'\\',0};
387     WCHAR *sxsdir, *name, *dst;
388     HRESULT hr = E_OUTOFMEMORY;
389     BOOL ret;
390     int len;
391
392     /* FIXME: handle catalog file */
393
394     if (!(sxsdir = build_sxs_path())) return E_OUTOFMEMORY;
395     if (!(name = build_policy_name( assembly ))) goto done;
396
397     len = strlenW( sxsdir );
398     len += strlenW( policiesW );
399     len += strlenW( name ) + 1;
400     len += strlenW( assembly->version );
401     len += strlenW( suffixW );
402
403     if (!(dst = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) goto done;
404     strcpyW( dst, sxsdir );
405     strcatW( dst, policiesW );
406     CreateDirectoryW( dst, NULL );
407     strcatW( dst, name );
408     CreateDirectoryW( dst, NULL );
409     strcatW( dst, backslashW );
410     strcatW( dst, assembly->version );
411     strcatW( dst, suffixW );
412
413     ret = CopyFileW( manifest, dst, FALSE );
414     HeapFree( GetProcessHeap(), 0, dst );
415     if (!ret)
416     {
417         hr = HRESULT_FROM_WIN32( GetLastError() );
418         WARN("failed to copy policy manifest file 0x%08x\n", hr);
419     }
420     hr = S_OK;
421
422 done:
423     HeapFree( GetProcessHeap(), 0, sxsdir );
424     HeapFree( GetProcessHeap(), 0, name );
425     return hr;
426 }
427
428 static WCHAR *build_source_filename( const WCHAR *manifest, struct file *file )
429 {
430     WCHAR *src;
431     const WCHAR *p;
432     int len;
433
434     p = strrchrW( manifest, '\\' );
435     if (!p) p = strrchrW( manifest, '/' );
436     if (!p) return strdupW( manifest );
437
438     len = p - manifest + 1;
439     if (!(src = HeapAlloc( GetProcessHeap(), 0, (len + strlenW( file->name ) + 1) * sizeof(WCHAR) )))
440         return NULL;
441
442     memcpy( src, manifest, len * sizeof(WCHAR) );
443     strcpyW( src + len, file->name );
444     return src;
445 }
446
447 static HRESULT install_assembly( const WCHAR *manifest, struct assembly *assembly )
448 {
449     static const WCHAR manifestsW[] = {'m','a','n','i','f','e','s','t','s','\\',0};
450     static const WCHAR suffixW[] = {'.','m','a','n','i','f','e','s','t',0};
451     static const WCHAR backslashW[] = {'\\',0};
452     WCHAR *sxsdir, *p, *name, *dst, *src;
453     struct file *file;
454     HRESULT hr = E_OUTOFMEMORY;
455     BOOL ret;
456     int len;
457
458     if (!(sxsdir = build_sxs_path())) return E_OUTOFMEMORY;
459     if (!(name = build_assembly_name( assembly ))) goto done;
460
461     len = strlenW( sxsdir );
462     len += strlenW( manifestsW );
463     len += strlenW( name );
464     len += strlenW( suffixW );
465     if (!(dst = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) goto done;
466     strcpyW( dst, sxsdir );
467     strcatW( dst, manifestsW );
468     strcatW( dst, name );
469     strcatW( dst, suffixW );
470
471     ret = CopyFileW( manifest, dst, FALSE );
472     HeapFree( GetProcessHeap(), 0, dst );
473     if (!ret)
474     {
475         hr = HRESULT_FROM_WIN32( GetLastError() );
476         WARN("failed to copy manifest file 0x%08x\n", hr);
477         goto done;
478     }
479
480     /* FIXME: this should be a transaction */
481     LIST_FOR_EACH_ENTRY( file, &assembly->files, struct file, entry )
482     {
483         if (!(src = build_source_filename( manifest, file )))
484         {
485             hr = E_OUTOFMEMORY;
486             goto done;
487         }
488         len = strlenW( sxsdir ) + strlenW( name ) + strlenW( file->name );
489         if (!(dst = HeapAlloc( GetProcessHeap(), 0, (len + 2) * sizeof(WCHAR) )))
490         {
491             HeapFree( GetProcessHeap(), 0, src );
492             hr = E_OUTOFMEMORY;
493             goto done;
494         }
495         strcpyW( dst, sxsdir );
496         strcatW( dst, name );
497         CreateDirectoryW( dst, NULL );
498
499         strcatW( dst, backslashW );
500         strcatW( dst, file->name );
501         for (p = dst; *p; p++) *p = tolowerW( *p );
502
503         ret = CopyFileW( src, dst, FALSE );
504         HeapFree( GetProcessHeap(), 0, src );
505         HeapFree( GetProcessHeap(), 0, dst );
506         if (!ret)
507         {
508             hr = HRESULT_FROM_WIN32( GetLastError() );
509             WARN("failed to copy file 0x%08x\n", hr);
510             goto done;
511         }
512     }
513     hr = S_OK;
514
515 done:
516     HeapFree( GetProcessHeap(), 0, sxsdir );
517     HeapFree( GetProcessHeap(), 0, name );
518     return hr;
519 }
520
521 static HRESULT WINAPI cache_InstallAssembly(
522     IAssemblyCache *iface,
523     DWORD flags,
524     LPCWSTR path,
525     LPCFUSION_INSTALL_REFERENCE ref )
526 {
527     static const WCHAR policyW[] = {'w','i','n','3','2','-','p','o','l','i','c','y',0};
528     HRESULT hr, init;
529     IXMLDOMDocument *doc = NULL;
530     struct assembly *assembly = NULL;
531     BSTR str;
532     VARIANT var;
533     VARIANT_BOOL b;
534
535     TRACE("%p, 0x%08x, %s, %p\n", iface, flags, debugstr_w(path), ref);
536
537     init = CoInitialize( NULL );
538
539     hr = CoCreateInstance( &CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void **)&doc );
540     if (hr != S_OK)
541         goto done;
542
543     str = SysAllocString( path );
544     VariantInit( &var );
545     V_VT( &var ) = VT_BSTR;
546     V_BSTR( &var ) = str;
547     hr = IXMLDOMDocument_load( doc, var, &b );
548     SysFreeString( str );
549     if (hr != S_OK) goto done;
550     if (!b)
551     {
552         WARN("failed to load manifest\n");
553         hr = S_FALSE;
554         goto done;
555     }
556
557     hr = parse_assembly( doc, &assembly );
558     if (hr != S_OK)
559         goto done;
560
561     /* FIXME: verify name attributes */
562
563     if (!strcmpW( assembly->type, policyW ))
564         hr = install_policy( path, assembly );
565     else
566         hr = install_assembly( path, assembly );
567
568 done:
569     free_assembly( assembly );
570     if (doc) IXMLDOMDocument_Release( doc );
571
572     if (SUCCEEDED(init))
573         CoUninitialize();
574
575     return hr;
576 }
577
578 static const IAssemblyCacheVtbl cache_vtbl =
579 {
580     cache_QueryInterface,
581     cache_AddRef,
582     cache_Release,
583     cache_UninstallAssembly,
584     cache_QueryAssemblyInfo,
585     cache_CreateAssemblyCacheItem,
586     cache_Reserved,
587     cache_InstallAssembly
588 };
589
590 /******************************************************************
591  *  CreateAssemblyCache   (SXS.@)
592  */
593 HRESULT WINAPI CreateAssemblyCache( IAssemblyCache **obj, DWORD reserved )
594 {
595     struct cache *cache;
596
597     TRACE("%p, %u\n", obj, reserved);
598
599     if (!obj)
600         return E_INVALIDARG;
601
602     *obj = NULL;
603
604     cache = HeapAlloc( GetProcessHeap(), 0, sizeof(struct cache) );
605     if (!cache)
606         return E_OUTOFMEMORY;
607
608     cache->vtbl = &cache_vtbl;
609     cache->refs = 1;
610
611     *obj = (IAssemblyCache *)cache;
612     return S_OK;
613 }