windows.networking.hostname/tests: Check if passed HSTRING is duplicated.
[wine.git] / dlls / msi / streams.c
blob4c23a97d10f74d576cc6dc465a755a3500175abc
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2007 James Hawkins
5 * Copyright 2015 Hans Leidekker for CodeWeavers
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.
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.
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
22 #include <stdarg.h>
24 #define COBJMACROS
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"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
39 #define NUM_STREAMS_COLS 2
41 struct streams_view
43 MSIVIEW view;
44 MSIDATABASE *db;
45 UINT num_cols;
48 static BOOL streams_resize_table( MSIDATABASE *db, UINT size )
50 if (!db->num_streams_allocated)
52 if (!(db->streams = calloc( size, sizeof(MSISTREAM) ))) return FALSE;
53 db->num_streams_allocated = size;
54 return TRUE;
56 while (size >= db->num_streams_allocated)
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;
65 return TRUE;
68 static UINT STREAMS_fetch_int(struct tagMSIVIEW *view, UINT row, UINT col, UINT *val)
70 struct streams_view *sv = (struct streams_view *)view;
72 TRACE("(%p, %d, %d, %p)\n", view, row, col, val);
74 if (col != 1)
75 return ERROR_INVALID_PARAMETER;
77 if (row >= sv->db->num_streams)
78 return ERROR_NO_MORE_ITEMS;
80 *val = sv->db->streams[row].str_index;
82 return ERROR_SUCCESS;
85 static UINT STREAMS_fetch_stream(struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
87 struct streams_view *sv = (struct streams_view *)view;
88 LARGE_INTEGER pos;
89 HRESULT hr;
91 TRACE("(%p, %d, %d, %p)\n", view, row, col, stm);
93 if (row >= sv->db->num_streams)
94 return ERROR_FUNCTION_FAILED;
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;
101 *stm = sv->db->streams[row].stream;
102 IStream_AddRef( *stm );
104 return ERROR_SUCCESS;
107 static UINT STREAMS_set_string( struct tagMSIVIEW *view, UINT row, UINT col, const WCHAR *val, int len )
109 ERR("Cannot modify primary key.\n");
110 return ERROR_FUNCTION_FAILED;
113 static UINT STREAMS_set_stream( MSIVIEW *view, UINT row, UINT col, IStream *stream )
115 struct streams_view *sv = (struct streams_view *)view;
116 IStream *prev;
118 TRACE("view %p, row %u, col %u, stream %p.\n", view, row, col, stream);
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;
126 static UINT STREAMS_set_row(struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask)
128 struct streams_view *sv = (struct streams_view *)view;
130 TRACE("(%p, %d, %p, %08x)\n", view, row, rec, mask);
132 if (row > sv->db->num_streams || mask >= (1 << sv->num_cols))
133 return ERROR_INVALID_PARAMETER;
135 if (mask & 1)
137 const WCHAR *name = MSI_RecordGetString( rec, 1 );
139 if (!name) return ERROR_INVALID_PARAMETER;
140 sv->db->streams[row].str_index = msi_add_string( sv->db->strings, name, -1, FALSE );
142 if (mask & 2)
144 IStream *old, *new;
145 HRESULT hr;
146 UINT r;
148 r = MSI_RecordGetIStream( rec, 2, &new );
149 if (r != ERROR_SUCCESS)
150 return r;
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 ))
157 return ERROR_FUNCTION_FAILED;
159 if (old) IStream_Release( old );
162 return ERROR_SUCCESS;
165 static UINT streams_find_row( struct streams_view *sv, MSIRECORD *rec, UINT *row )
167 const WCHAR *str;
168 UINT r, i, id, val;
170 str = MSI_RecordGetString( rec, 1 );
171 r = msi_string2id( sv->db->strings, str, -1, &id );
172 if (r != ERROR_SUCCESS)
173 return r;
175 for (i = 0; i < sv->db->num_streams; i++)
177 STREAMS_fetch_int( &sv->view, i, 1, &val );
179 if (val == id)
181 if (row) *row = i;
182 return ERROR_SUCCESS;
186 return ERROR_FUNCTION_FAILED;
189 static UINT STREAMS_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary)
191 struct streams_view *sv = (struct streams_view *)view;
192 UINT i, r, num_rows = sv->db->num_streams + 1;
194 TRACE("(%p, %p, %d, %d)\n", view, rec, row, temporary);
196 r = streams_find_row( sv, rec, NULL );
197 if (r == ERROR_SUCCESS)
198 return ERROR_FUNCTION_FAILED;
200 if (!streams_resize_table( sv->db, num_rows ))
201 return ERROR_FUNCTION_FAILED;
203 if (row == -1)
204 row = num_rows - 1;
206 /* shift the rows to make room for the new row */
207 for (i = num_rows - 1; i > row; i--)
209 sv->db->streams[i] = sv->db->streams[i - 1];
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;
216 return r;
219 static UINT STREAMS_delete_row(struct tagMSIVIEW *view, UINT row)
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;
227 TRACE("(%p %d)\n", view, row);
229 if (!db->num_streams || row > num_rows)
230 return ERROR_FUNCTION_FAILED;
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 );
236 for (i = row; i < num_rows; i++)
237 db->streams[i] = db->streams[i + 1];
238 db->num_streams = num_rows;
240 hr = IStorage_DestroyElement( db->storage, encname );
241 free( encname );
242 return FAILED( hr ) ? ERROR_FUNCTION_FAILED : ERROR_SUCCESS;
245 static UINT STREAMS_execute(struct tagMSIVIEW *view, MSIRECORD *record)
247 TRACE("(%p, %p)\n", view, record);
248 return ERROR_SUCCESS;
251 static UINT STREAMS_close(struct tagMSIVIEW *view)
253 TRACE("(%p)\n", view);
254 return ERROR_SUCCESS;
257 static UINT STREAMS_get_dimensions(struct tagMSIVIEW *view, UINT *rows, UINT *cols)
259 struct streams_view *sv = (struct streams_view *)view;
261 TRACE("(%p, %p, %p)\n", view, rows, cols);
263 if (cols) *cols = sv->num_cols;
264 if (rows) *rows = sv->db->num_streams;
266 return ERROR_SUCCESS;
269 static UINT STREAMS_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name,
270 UINT *type, BOOL *temporary, LPCWSTR *table_name )
272 struct streams_view *sv = (struct streams_view *)view;
274 TRACE("(%p, %d, %p, %p, %p, %p)\n", view, n, name, type, temporary, table_name);
276 if (!n || n > sv->num_cols)
277 return ERROR_INVALID_PARAMETER;
279 switch (n)
281 case 1:
282 if (name) *name = L"Name";
283 if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MAX_STREAM_NAME_LEN;
284 break;
286 case 2:
287 if (name) *name = L"Data";
288 if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MSITYPE_NULLABLE;
289 break;
291 if (table_name) *table_name = L"_Streams";
292 if (temporary) *temporary = FALSE;
293 return ERROR_SUCCESS;
296 static UINT streams_modify_update(struct tagMSIVIEW *view, MSIRECORD *rec)
298 struct streams_view *sv = (struct streams_view *)view;
299 UINT r, row;
301 r = streams_find_row(sv, rec, &row);
302 if (r != ERROR_SUCCESS)
303 return ERROR_FUNCTION_FAILED;
305 return STREAMS_set_row( view, row, rec, (1 << sv->num_cols) - 1 );
308 static UINT streams_modify_assign(struct tagMSIVIEW *view, MSIRECORD *rec)
310 struct streams_view *sv = (struct streams_view *)view;
311 UINT r;
313 r = streams_find_row( sv, rec, NULL );
314 if (r == ERROR_SUCCESS)
315 return streams_modify_update(view, rec);
317 return STREAMS_insert_row(view, rec, -1, FALSE);
320 static UINT STREAMS_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row)
322 UINT r;
324 TRACE("%p %d %p\n", view, eModifyMode, rec);
326 switch (eModifyMode)
328 case MSIMODIFY_ASSIGN:
329 r = streams_modify_assign(view, rec);
330 break;
332 case MSIMODIFY_INSERT:
333 r = STREAMS_insert_row(view, rec, -1, FALSE);
334 break;
336 case MSIMODIFY_UPDATE:
337 r = streams_modify_update(view, rec);
338 break;
340 case MSIMODIFY_DELETE:
341 r = STREAMS_delete_row(view, row - 1);
342 break;
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;
356 default:
357 r = ERROR_INVALID_DATA;
360 return r;
363 static UINT STREAMS_delete(struct tagMSIVIEW *view)
365 struct streams_view *sv = (struct streams_view *)view;
367 TRACE("(%p)\n", view);
369 free(sv);
370 return ERROR_SUCCESS;
373 static const MSIVIEWOPS streams_ops =
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,
396 static HRESULT open_stream( MSIDATABASE *db, const WCHAR *name, IStream **stream )
398 HRESULT hr;
400 hr = IStorage_OpenStream( db->storage, name, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, stream );
401 if (FAILED( hr ))
403 MSITRANSFORM *transform;
405 LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry )
407 hr = IStorage_OpenStream( transform->stg, name, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, stream );
408 if (SUCCEEDED( hr ))
409 break;
412 return hr;
415 static MSISTREAM *find_stream( MSIDATABASE *db, const WCHAR *name )
417 UINT r, id, i;
419 r = msi_string2id( db->strings, name, -1, &id );
420 if (r != ERROR_SUCCESS)
421 return NULL;
423 for (i = 0; i < db->num_streams; i++)
425 if (db->streams[i].str_index == id) return &db->streams[i];
427 return NULL;
430 static UINT append_stream( MSIDATABASE *db, const WCHAR *name, IStream *stream )
432 UINT i = db->num_streams;
434 if (!streams_resize_table( db, db->num_streams + 1 ))
435 return ERROR_OUTOFMEMORY;
437 db->streams[i].str_index = msi_add_string( db->strings, name, -1, FALSE );
438 db->streams[i].stream = stream;
439 db->num_streams++;
441 TRACE("added %s\n", debugstr_w( name ));
442 return ERROR_SUCCESS;
445 static UINT load_streams( MSIDATABASE *db )
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;
455 hr = IStorage_EnumElements( db->storage, 0, NULL, 0, &stgenum );
456 if (FAILED( hr ))
457 return ERROR_FUNCTION_FAILED;
459 for (;;)
461 count = 0;
462 hr = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
463 if (FAILED( hr ) || !count)
464 break;
466 /* table streams are not in the _Streams table */
467 if (stat.type != STGTY_STREAM || *stat.pwcsName == 0x4840)
469 CoTaskMemFree( stat.pwcsName );
470 continue;
472 decode_streamname( stat.pwcsName, decoded );
473 if (find_stream( db, decoded ))
475 CoTaskMemFree( stat.pwcsName );
476 continue;
478 TRACE("found new stream %s\n", debugstr_w( decoded ));
480 hr = open_stream( db, stat.pwcsName, &stream );
481 CoTaskMemFree( stat.pwcsName );
482 if (FAILED( hr ))
484 ERR( "unable to open stream %#lx\n", hr );
485 r = ERROR_FUNCTION_FAILED;
486 break;
489 r = append_stream( db, decoded, stream );
490 if (r != ERROR_SUCCESS)
491 break;
494 TRACE("loaded %u streams\n", db->num_streams);
495 IEnumSTATSTG_Release( stgenum );
496 return r;
499 UINT msi_get_stream( MSIDATABASE *db, const WCHAR *name, IStream **ret )
501 MSISTREAM *stream;
502 WCHAR *encname;
503 HRESULT hr;
504 UINT r;
506 if ((stream = find_stream( db, name )))
508 LARGE_INTEGER pos;
510 pos.QuadPart = 0;
511 hr = IStream_Seek( stream->stream, pos, STREAM_SEEK_SET, NULL );
512 if (FAILED( hr ))
513 return ERROR_FUNCTION_FAILED;
515 *ret = stream->stream;
516 IStream_AddRef( *ret );
517 return ERROR_SUCCESS;
520 if (!(encname = encode_streamname( FALSE, name )))
521 return ERROR_OUTOFMEMORY;
523 hr = open_stream( db, encname, ret );
524 free( encname );
525 if (FAILED( hr ))
526 return ERROR_FUNCTION_FAILED;
528 r = append_stream( db, name, *ret );
529 if (r != ERROR_SUCCESS)
531 IStream_Release( *ret );
532 return r;
535 IStream_AddRef( *ret );
536 return ERROR_SUCCESS;
539 UINT STREAMS_CreateView(MSIDATABASE *db, MSIVIEW **view)
541 struct streams_view *sv;
542 UINT r;
544 TRACE("(%p, %p)\n", db, view);
546 r = load_streams( db );
547 if (r != ERROR_SUCCESS)
548 return r;
550 if (!(sv = calloc( 1, sizeof(*sv) )))
551 return ERROR_OUTOFMEMORY;
553 sv->view.ops = &streams_ops;
554 sv->num_cols = NUM_STREAMS_COLS;
555 sv->db = db;
557 *view = (MSIVIEW *)sv;
559 return ERROR_SUCCESS;
562 static HRESULT write_stream( IStream *dst, IStream *src )
564 HRESULT hr;
565 char buf[4096];
566 STATSTG stat;
567 LARGE_INTEGER pos;
568 ULONG count;
569 UINT size;
571 hr = IStream_Stat( src, &stat, STATFLAG_NONAME );
572 if (FAILED( hr )) return hr;
574 hr = IStream_SetSize( dst, stat.cbSize );
575 if (FAILED( hr )) return hr;
577 pos.QuadPart = 0;
578 hr = IStream_Seek( dst, pos, STREAM_SEEK_SET, NULL );
579 if (FAILED( hr )) return hr;
581 for (;;)
583 size = min( sizeof(buf), stat.cbSize.QuadPart );
584 hr = IStream_Read( src, buf, size, &count );
585 if (FAILED( hr ) || count != size)
587 WARN( "failed to read stream: %#lx\n", hr );
588 return E_INVALIDARG;
590 stat.cbSize.QuadPart -= count;
591 if (count)
593 size = count;
594 hr = IStream_Write( dst, buf, size, &count );
595 if (FAILED( hr ) || count != size)
597 WARN( "failed to write stream: %#lx\n", hr );
598 return E_INVALIDARG;
601 if (!stat.cbSize.QuadPart) break;
604 return S_OK;
607 UINT msi_commit_streams( MSIDATABASE *db )
609 UINT i;
610 const WCHAR *name;
611 WCHAR *encname;
612 IStream *stream;
613 HRESULT hr;
615 TRACE("got %u streams\n", db->num_streams);
617 for (i = 0; i < db->num_streams; i++)
619 name = msi_string_lookup( db->strings, db->streams[i].str_index, NULL );
620 if (!wcscmp( name, L"\5SummaryInformation" )) continue;
622 if (!(encname = encode_streamname( FALSE, name ))) return ERROR_OUTOFMEMORY;
623 TRACE("saving stream %s as %s\n", debugstr_w(name), debugstr_w(encname));
625 hr = IStorage_CreateStream( db->storage, encname, STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &stream );
626 if (SUCCEEDED( hr ))
628 hr = write_stream( stream, db->streams[i].stream );
629 if (FAILED( hr ))
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;
636 hr = IStream_Commit( stream, 0 );
637 IStream_Release( stream );
638 if (FAILED( hr ))
640 ERR( "failed to commit stream %s (hr = %#lx)\n", debugstr_w(encname), hr );
641 free( encname );
642 return ERROR_FUNCTION_FAILED;
645 else if (hr != STG_E_FILEALREADYEXISTS)
647 ERR( "failed to create stream %s (hr = %#lx)\n", debugstr_w(encname), hr );
648 free( encname );
649 return ERROR_FUNCTION_FAILED;
651 free( encname );
654 return ERROR_SUCCESS;