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