xref: /reactos/dll/win32/msi/streams.c (revision 0c2cdcae)
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2007 James Hawkins
5  * Copyright 2015 Hans Leidekker for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include <stdarg.h>
23 
24 #define COBJMACROS
25 
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winerror.h"
29 #include "msi.h"
30 #include "msiquery.h"
31 #include "objbase.h"
32 #include "msipriv.h"
33 #include "query.h"
34 
35 #include "wine/debug.h"
36 
37 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
38 
39 #define NUM_STREAMS_COLS    2
40 
41 struct streams_view
42 {
43     MSIVIEW view;
44     MSIDATABASE *db;
45     UINT num_cols;
46 };
47 
48 static BOOL streams_resize_table( MSIDATABASE *db, UINT size )
49 {
50     if (!db->num_streams_allocated)
51     {
52         if (!(db->streams = calloc( size, sizeof(MSISTREAM) ))) return FALSE;
53         db->num_streams_allocated = size;
54         return TRUE;
55     }
56     while (size >= db->num_streams_allocated)
57     {
58         MSISTREAM *tmp;
59         UINT new_size = db->num_streams_allocated * 2;
60         if (!(tmp = realloc( db->streams, new_size * sizeof(*tmp) ))) return FALSE;
61         memset( tmp + db->num_streams_allocated, 0, (new_size - db->num_streams_allocated) * sizeof(*tmp) );
62         db->streams = tmp;
63         db->num_streams_allocated = new_size;
64     }
65     return TRUE;
66 }
67 
68 static UINT STREAMS_fetch_int(struct tagMSIVIEW *view, UINT row, UINT col, UINT *val)
69 {
70     struct streams_view *sv = (struct streams_view *)view;
71 
72     TRACE("(%p, %d, %d, %p)\n", view, row, col, val);
73 
74     if (col != 1)
75         return ERROR_INVALID_PARAMETER;
76 
77     if (row >= sv->db->num_streams)
78         return ERROR_NO_MORE_ITEMS;
79 
80     *val = sv->db->streams[row].str_index;
81 
82     return ERROR_SUCCESS;
83 }
84 
85 static UINT STREAMS_fetch_stream(struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
86 {
87     struct streams_view *sv = (struct streams_view *)view;
88     LARGE_INTEGER pos;
89     HRESULT hr;
90 
91     TRACE("(%p, %d, %d, %p)\n", view, row, col, stm);
92 
93     if (row >= sv->db->num_streams)
94         return ERROR_FUNCTION_FAILED;
95 
96     pos.QuadPart = 0;
97     hr = IStream_Seek( sv->db->streams[row].stream, pos, STREAM_SEEK_SET, NULL );
98     if (FAILED( hr ))
99         return ERROR_FUNCTION_FAILED;
100 
101     *stm = sv->db->streams[row].stream;
102     IStream_AddRef( *stm );
103 
104     return ERROR_SUCCESS;
105 }
106 
107 static UINT STREAMS_set_string( struct tagMSIVIEW *view, UINT row, UINT col, const WCHAR *val, int len )
108 {
109     ERR("Cannot modify primary key.\n");
110     return ERROR_FUNCTION_FAILED;
111 }
112 
113 static UINT STREAMS_set_stream( MSIVIEW *view, UINT row, UINT col, IStream *stream )
114 {
115     struct streams_view *sv = (struct streams_view *)view;
116     IStream *prev;
117 
118     TRACE("view %p, row %u, col %u, stream %p.\n", view, row, col, stream);
119 
120     prev = sv->db->streams[row].stream;
121     IStream_AddRef(sv->db->streams[row].stream = stream);
122     if (prev) IStream_Release(prev);
123     return ERROR_SUCCESS;
124 }
125 
126 static UINT STREAMS_set_row(struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask)
127 {
128     struct streams_view *sv = (struct streams_view *)view;
129 
130     TRACE("(%p, %d, %p, %08x)\n", view, row, rec, mask);
131 
132     if (row > sv->db->num_streams || mask >= (1 << sv->num_cols))
133         return ERROR_INVALID_PARAMETER;
134 
135     if (mask & 1)
136     {
137         const WCHAR *name = MSI_RecordGetString( rec, 1 );
138 
139         if (!name) return ERROR_INVALID_PARAMETER;
140         sv->db->streams[row].str_index = msi_add_string( sv->db->strings, name, -1, FALSE );
141     }
142     if (mask & 2)
143     {
144         IStream *old, *new;
145         HRESULT hr;
146         UINT r;
147 
148         r = MSI_RecordGetIStream( rec, 2, &new );
149         if (r != ERROR_SUCCESS)
150             return r;
151 
152         old = sv->db->streams[row].stream;
153         hr = IStream_QueryInterface( new, &IID_IStream, (void **)&sv->db->streams[row].stream );
154         IStream_Release( new );
155         if (FAILED( hr ))
156         {
157             return ERROR_FUNCTION_FAILED;
158         }
159         if (old) IStream_Release( old );
160     }
161 
162     return ERROR_SUCCESS;
163 }
164 
165 static UINT streams_find_row( struct streams_view *sv, MSIRECORD *rec, UINT *row )
166 {
167     const WCHAR *str;
168     UINT r, i, id, val;
169 
170     str = MSI_RecordGetString( rec, 1 );
171     r = msi_string2id( sv->db->strings, str, -1, &id );
172     if (r != ERROR_SUCCESS)
173         return r;
174 
175     for (i = 0; i < sv->db->num_streams; i++)
176     {
177         STREAMS_fetch_int( &sv->view, i, 1, &val );
178 
179         if (val == id)
180         {
181             if (row) *row = i;
182             return ERROR_SUCCESS;
183         }
184     }
185 
186     return ERROR_FUNCTION_FAILED;
187 }
188 
189 static UINT STREAMS_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary)
190 {
191     struct streams_view *sv = (struct streams_view *)view;
192     UINT i, r, num_rows = sv->db->num_streams + 1;
193 
194     TRACE("(%p, %p, %d, %d)\n", view, rec, row, temporary);
195 
196     r = streams_find_row( sv, rec, NULL );
197     if (r == ERROR_SUCCESS)
198         return ERROR_FUNCTION_FAILED;
199 
200     if (!streams_resize_table( sv->db, num_rows ))
201         return ERROR_FUNCTION_FAILED;
202 
203     if (row == -1)
204         row = num_rows - 1;
205 
206     /* shift the rows to make room for the new row */
207     for (i = num_rows - 1; i > row; i--)
208     {
209         sv->db->streams[i] = sv->db->streams[i - 1];
210     }
211 
212     r = STREAMS_set_row( view, row, rec, (1 << sv->num_cols) - 1 );
213     if (r == ERROR_SUCCESS)
214         sv->db->num_streams = num_rows;
215 
216     return r;
217 }
218 
219 static UINT STREAMS_delete_row(struct tagMSIVIEW *view, UINT row)
220 {
221     MSIDATABASE *db = ((struct streams_view *)view)->db;
222     UINT i, num_rows = db->num_streams - 1;
223     const WCHAR *name;
224     WCHAR *encname;
225     HRESULT hr;
226 
227     TRACE("(%p %d)\n", view, row);
228 
229     if (!db->num_streams || row > num_rows)
230         return ERROR_FUNCTION_FAILED;
231 
232     name = msi_string_lookup( db->strings, db->streams[row].str_index, NULL );
233     if (!(encname = encode_streamname( FALSE, name ))) return ERROR_OUTOFMEMORY;
234     IStream_Release( db->streams[row].stream );
235 
236     for (i = row; i < num_rows; i++)
237         db->streams[i] = db->streams[i + 1];
238     db->num_streams = num_rows;
239 
240     hr = IStorage_DestroyElement( db->storage, encname );
241     free( encname );
242     return FAILED( hr ) ? ERROR_FUNCTION_FAILED : ERROR_SUCCESS;
243 }
244 
245 static UINT STREAMS_execute(struct tagMSIVIEW *view, MSIRECORD *record)
246 {
247     TRACE("(%p, %p)\n", view, record);
248     return ERROR_SUCCESS;
249 }
250 
251 static UINT STREAMS_close(struct tagMSIVIEW *view)
252 {
253     TRACE("(%p)\n", view);
254     return ERROR_SUCCESS;
255 }
256 
257 static UINT STREAMS_get_dimensions(struct tagMSIVIEW *view, UINT *rows, UINT *cols)
258 {
259     struct streams_view *sv = (struct streams_view *)view;
260 
261     TRACE("(%p, %p, %p)\n", view, rows, cols);
262 
263     if (cols) *cols = sv->num_cols;
264     if (rows) *rows = sv->db->num_streams;
265 
266     return ERROR_SUCCESS;
267 }
268 
269 static UINT STREAMS_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name,
270                                      UINT *type, BOOL *temporary, LPCWSTR *table_name )
271 {
272     struct streams_view *sv = (struct streams_view *)view;
273 
274     TRACE("(%p, %d, %p, %p, %p, %p)\n", view, n, name, type, temporary, table_name);
275 
276     if (!n || n > sv->num_cols)
277         return ERROR_INVALID_PARAMETER;
278 
279     switch (n)
280     {
281     case 1:
282         if (name) *name = L"Name";
283         if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MAX_STREAM_NAME_LEN;
284         break;
285 
286     case 2:
287         if (name) *name = L"Data";
288         if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MSITYPE_NULLABLE;
289         break;
290     }
291     if (table_name) *table_name = L"_Streams";
292     if (temporary) *temporary = FALSE;
293     return ERROR_SUCCESS;
294 }
295 
296 static UINT streams_modify_update(struct tagMSIVIEW *view, MSIRECORD *rec)
297 {
298     struct streams_view *sv = (struct streams_view *)view;
299     UINT r, row;
300 
301     r = streams_find_row(sv, rec, &row);
302     if (r != ERROR_SUCCESS)
303         return ERROR_FUNCTION_FAILED;
304 
305     return STREAMS_set_row( view, row, rec, (1 << sv->num_cols) - 1 );
306 }
307 
308 static UINT streams_modify_assign(struct tagMSIVIEW *view, MSIRECORD *rec)
309 {
310     struct streams_view *sv = (struct streams_view *)view;
311     UINT r;
312 
313     r = streams_find_row( sv, rec, NULL );
314     if (r == ERROR_SUCCESS)
315         return streams_modify_update(view, rec);
316 
317     return STREAMS_insert_row(view, rec, -1, FALSE);
318 }
319 
320 static UINT STREAMS_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row)
321 {
322     UINT r;
323 
324     TRACE("%p %d %p\n", view, eModifyMode, rec);
325 
326     switch (eModifyMode)
327     {
328     case MSIMODIFY_ASSIGN:
329         r = streams_modify_assign(view, rec);
330         break;
331 
332     case MSIMODIFY_INSERT:
333         r = STREAMS_insert_row(view, rec, -1, FALSE);
334         break;
335 
336     case MSIMODIFY_UPDATE:
337         r = streams_modify_update(view, rec);
338         break;
339 
340     case MSIMODIFY_DELETE:
341         r = STREAMS_delete_row(view, row - 1);
342         break;
343 
344     case MSIMODIFY_VALIDATE_NEW:
345     case MSIMODIFY_INSERT_TEMPORARY:
346     case MSIMODIFY_REFRESH:
347     case MSIMODIFY_REPLACE:
348     case MSIMODIFY_MERGE:
349     case MSIMODIFY_VALIDATE:
350     case MSIMODIFY_VALIDATE_FIELD:
351     case MSIMODIFY_VALIDATE_DELETE:
352         FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
353         r = ERROR_CALL_NOT_IMPLEMENTED;
354         break;
355 
356     default:
357         r = ERROR_INVALID_DATA;
358     }
359 
360     return r;
361 }
362 
363 static UINT STREAMS_delete(struct tagMSIVIEW *view)
364 {
365     struct streams_view *sv = (struct streams_view *)view;
366 
367     TRACE("(%p)\n", view);
368 
369     free(sv);
370     return ERROR_SUCCESS;
371 }
372 
373 static const MSIVIEWOPS streams_ops =
374 {
375     STREAMS_fetch_int,
376     STREAMS_fetch_stream,
377     NULL,
378     STREAMS_set_string,
379     STREAMS_set_stream,
380     STREAMS_set_row,
381     STREAMS_insert_row,
382     STREAMS_delete_row,
383     STREAMS_execute,
384     STREAMS_close,
385     STREAMS_get_dimensions,
386     STREAMS_get_column_info,
387     STREAMS_modify,
388     STREAMS_delete,
389     NULL,
390     NULL,
391     NULL,
392     NULL,
393     NULL,
394 };
395 
396 static HRESULT open_stream( MSIDATABASE *db, const WCHAR *name, IStream **stream )
397 {
398     HRESULT hr;
399 
400     hr = IStorage_OpenStream( db->storage, name, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, stream );
401     if (FAILED( hr ))
402     {
403         MSITRANSFORM *transform;
404 
405         LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry )
406         {
407             hr = IStorage_OpenStream( transform->stg, name, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, stream );
408             if (SUCCEEDED( hr ))
409                 break;
410         }
411     }
412     return hr;
413 }
414 
415 static MSISTREAM *find_stream( MSIDATABASE *db, const WCHAR *name )
416 {
417     UINT r, id, i;
418 
419     r = msi_string2id( db->strings, name, -1, &id );
420     if (r != ERROR_SUCCESS)
421         return NULL;
422 
423     for (i = 0; i < db->num_streams; i++)
424     {
425         if (db->streams[i].str_index == id) return &db->streams[i];
426     }
427     return NULL;
428 }
429 
430 static UINT append_stream( MSIDATABASE *db, const WCHAR *name, IStream *stream )
431 {
432     UINT i = db->num_streams;
433 
434     if (!streams_resize_table( db, db->num_streams + 1 ))
435         return ERROR_OUTOFMEMORY;
436 
437     db->streams[i].str_index = msi_add_string( db->strings, name, -1, FALSE );
438     db->streams[i].stream = stream;
439     db->num_streams++;
440 
441     TRACE("added %s\n", debugstr_w( name ));
442     return ERROR_SUCCESS;
443 }
444 
445 static UINT load_streams( MSIDATABASE *db )
446 {
447     WCHAR decoded[MAX_STREAM_NAME_LEN + 1];
448     IEnumSTATSTG *stgenum;
449     STATSTG stat;
450     HRESULT hr;
451     ULONG count;
452     UINT r = ERROR_SUCCESS;
453     IStream *stream;
454 
455     hr = IStorage_EnumElements( db->storage, 0, NULL, 0, &stgenum );
456     if (FAILED( hr ))
457         return ERROR_FUNCTION_FAILED;
458 
459     for (;;)
460     {
461         count = 0;
462         hr = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
463         if (FAILED( hr ) || !count)
464             break;
465 
466         /* table streams are not in the _Streams table */
467         if (stat.type != STGTY_STREAM || *stat.pwcsName == 0x4840)
468         {
469             CoTaskMemFree( stat.pwcsName );
470             continue;
471         }
472         decode_streamname( stat.pwcsName, decoded );
473         if (find_stream( db, decoded ))
474         {
475             CoTaskMemFree( stat.pwcsName );
476             continue;
477         }
478         TRACE("found new stream %s\n", debugstr_w( decoded ));
479 
480         hr = open_stream( db, stat.pwcsName, &stream );
481         CoTaskMemFree( stat.pwcsName );
482         if (FAILED( hr ))
483         {
484             ERR( "unable to open stream %#lx\n", hr );
485             r = ERROR_FUNCTION_FAILED;
486             break;
487         }
488 
489         r = append_stream( db, decoded, stream );
490         if (r != ERROR_SUCCESS)
491             break;
492     }
493 
494     TRACE("loaded %u streams\n", db->num_streams);
495     IEnumSTATSTG_Release( stgenum );
496     return r;
497 }
498 
499 UINT msi_get_stream( MSIDATABASE *db, const WCHAR *name, IStream **ret )
500 {
501     MSISTREAM *stream;
502     WCHAR *encname;
503     HRESULT hr;
504     UINT r;
505 
506     if ((stream = find_stream( db, name )))
507     {
508         LARGE_INTEGER pos;
509 
510         pos.QuadPart = 0;
511         hr = IStream_Seek( stream->stream, pos, STREAM_SEEK_SET, NULL );
512         if (FAILED( hr ))
513             return ERROR_FUNCTION_FAILED;
514 
515         *ret = stream->stream;
516         IStream_AddRef( *ret );
517         return ERROR_SUCCESS;
518     }
519 
520     if (!(encname = encode_streamname( FALSE, name )))
521         return ERROR_OUTOFMEMORY;
522 
523     hr = open_stream( db, encname, ret );
524     free( encname );
525     if (FAILED( hr ))
526         return ERROR_FUNCTION_FAILED;
527 
528     r = append_stream( db, name, *ret );
529     if (r != ERROR_SUCCESS)
530     {
531         IStream_Release( *ret );
532         return r;
533     }
534 
535     IStream_AddRef( *ret );
536     return ERROR_SUCCESS;
537 }
538 
539 UINT STREAMS_CreateView(MSIDATABASE *db, MSIVIEW **view)
540 {
541     struct streams_view *sv;
542     UINT r;
543 
544     TRACE("(%p, %p)\n", db, view);
545 
546     r = load_streams( db );
547     if (r != ERROR_SUCCESS)
548         return r;
549 
550     if (!(sv = calloc( 1, sizeof(*sv) )))
551         return ERROR_OUTOFMEMORY;
552 
553     sv->view.ops = &streams_ops;
554     sv->num_cols = NUM_STREAMS_COLS;
555     sv->db = db;
556 
557     *view = (MSIVIEW *)sv;
558 
559     return ERROR_SUCCESS;
560 }
561 
562 static HRESULT write_stream( IStream *dst, IStream *src )
563 {
564     HRESULT hr;
565     char buf[4096];
566     STATSTG stat;
567     LARGE_INTEGER pos;
568     ULONG count;
569     UINT size;
570 
571     hr = IStream_Stat( src, &stat, STATFLAG_NONAME );
572     if (FAILED( hr )) return hr;
573 
574     hr = IStream_SetSize( dst, stat.cbSize );
575     if (FAILED( hr )) return hr;
576 
577     pos.QuadPart = 0;
578     hr = IStream_Seek( dst, pos, STREAM_SEEK_SET, NULL );
579     if (FAILED( hr )) return hr;
580 
581     for (;;)
582     {
583         size = min( sizeof(buf), stat.cbSize.QuadPart );
584         hr = IStream_Read( src, buf, size, &count );
585         if (FAILED( hr ) || count != size)
586         {
587             WARN( "failed to read stream: %#lx\n", hr );
588             return E_INVALIDARG;
589         }
590         stat.cbSize.QuadPart -= count;
591         if (count)
592         {
593             size = count;
594             hr = IStream_Write( dst, buf, size, &count );
595             if (FAILED( hr ) || count != size)
596             {
597                 WARN( "failed to write stream: %#lx\n", hr );
598                 return E_INVALIDARG;
599             }
600         }
601         if (!stat.cbSize.QuadPart) break;
602     }
603 
604     return S_OK;
605 }
606 
607 UINT msi_commit_streams( MSIDATABASE *db )
608 {
609     UINT i;
610     const WCHAR *name;
611     WCHAR *encname;
612     IStream *stream;
613     HRESULT hr;
614 
615     TRACE("got %u streams\n", db->num_streams);
616 
617     for (i = 0; i < db->num_streams; i++)
618     {
619         name = msi_string_lookup( db->strings, db->streams[i].str_index, NULL );
620         if (!wcscmp( name, L"\5SummaryInformation" )) continue;
621 
622         if (!(encname = encode_streamname( FALSE, name ))) return ERROR_OUTOFMEMORY;
623         TRACE("saving stream %s as %s\n", debugstr_w(name), debugstr_w(encname));
624 
625         hr = IStorage_CreateStream( db->storage, encname, STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &stream );
626         if (SUCCEEDED( hr ))
627         {
628             hr = write_stream( stream, db->streams[i].stream );
629             if (FAILED( hr ))
630             {
631                 ERR( "failed to write stream %s (hr = %#lx)\n", debugstr_w(encname), hr );
632                 free( encname );
633                 IStream_Release( stream );
634                 return ERROR_FUNCTION_FAILED;
635             }
636             hr = IStream_Commit( stream, 0 );
637             IStream_Release( stream );
638             if (FAILED( hr ))
639             {
640                 ERR( "failed to commit stream %s (hr = %#lx)\n", debugstr_w(encname), hr );
641                 free( encname );
642                 return ERROR_FUNCTION_FAILED;
643             }
644         }
645         else if (hr != STG_E_FILEALREADYEXISTS)
646         {
647             ERR( "failed to create stream %s (hr = %#lx)\n", debugstr_w(encname), hr );
648             free( encname );
649             return ERROR_FUNCTION_FAILED;
650         }
651         free( encname );
652     }
653 
654     return ERROR_SUCCESS;
655 }
656