Added patches for msidb commandline utility (to read and write *.msi files).

This commit is contained in:
Sebastian Lackner 2015-09-23 13:00:53 +02:00
parent 4ad4553546
commit 456eb2f553
17 changed files with 1804 additions and 1 deletions

View File

@ -39,8 +39,9 @@ Wine. All those differences are also documented on the
Included bug fixes and improvements
-----------------------------------
**Bug fixes and features included in the next upcoming release [3]:**
**Bug fixes and features included in the next upcoming release [4]:**
* Add implementation for msidb commandline tool
* Codepage conversion should fail when destination length is < 0
* Ignore higher bits in selector for ThreadDescriptorTableEntry info query
* Return STATUS_INVALID_DEVICE_REQUEST when trying to call NtReadFile on directory

1
debian/changelog vendored
View File

@ -5,6 +5,7 @@ wine-staging (1.7.52) UNRELEASED; urgency=low
info query.
* Added patch to ensure codepage conversion fails when destination length is
smaller than zero.
* Added patches for msidb commandline utility (to read and write *.msi files).
* Removed patch to fix possible memory leak in netprofm init_networks (fixed
upstream).
* Removed patch for stub of dwmapi.DwmUpdateThumbnailProperties (accepted

View File

@ -0,0 +1,200 @@
From b2940fd680565f442be117c077547c62090cfd3c Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Tue, 15 Sep 2015 17:44:33 -0600
Subject: msidb: Add stub tool for manipulating MSI databases.
Signed-off-by: Erich E. Hoover <erich.e.hoover@wine-staging.com>
---
configure.ac | 1 +
programs/msidb/Makefile.in | 6 ++
programs/msidb/main.c | 153 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 160 insertions(+)
create mode 100644 programs/msidb/Makefile.in
create mode 100644 programs/msidb/main.c
diff --git a/configure.ac b/configure.ac
index 135268f..ed0a472 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3503,6 +3503,7 @@ WINE_CONFIG_PROGRAM(ipconfig,,[install,po])
WINE_CONFIG_PROGRAM(lodctr,,[install])
WINE_CONFIG_PROGRAM(mofcomp,,[install])
WINE_CONFIG_PROGRAM(mshta,,[install])
+WINE_CONFIG_PROGRAM(msidb,,[install,installbin])
WINE_CONFIG_PROGRAM(msiexec,,[install,installbin,manpage])
WINE_CONFIG_PROGRAM(net,,[install,po])
WINE_CONFIG_PROGRAM(netsh,,[install])
diff --git a/programs/msidb/Makefile.in b/programs/msidb/Makefile.in
new file mode 100644
index 0000000..e83de8c
--- /dev/null
+++ b/programs/msidb/Makefile.in
@@ -0,0 +1,6 @@
+MODULE = msidb.exe
+APPMODE = -mconsole -municode
+IMPORTS = msi
+
+C_SRCS = \
+ main.c
diff --git a/programs/msidb/main.c b/programs/msidb/main.c
new file mode 100644
index 0000000..43cc519
--- /dev/null
+++ b/programs/msidb/main.c
@@ -0,0 +1,153 @@
+/*
+ * msidb - command line tool for assembling MSI packages
+ *
+ * Copyright 2015 Erich E. Hoover
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define WIN32_LEAN_AND_MEAN
+
+#include <stdlib.h>
+#include <windows.h>
+#include <msi.h>
+#include <msiquery.h>
+
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msidb);
+
+struct msidb_state
+{
+ WCHAR *database_file;
+ WCHAR *table_folder;
+ MSIHANDLE database_handle;
+ BOOL create_database;
+};
+
+static void show_usage( void )
+{
+ WINE_MESSAGE(
+ "Usage: msidb [OPTION]...[OPTION]...\n"
+ "Options:\n"
+ " -? Show this usage message and exit.\n"
+ " -c Create database file (instead of opening existing file).\n"
+ " -d package.msi Path to the database file.\n"
+ " -f folder Folder in which to open/save the tables.\n"
+ );
+}
+
+static int valid_state( struct msidb_state *state )
+{
+ if (state->database_file == NULL)
+ {
+ FIXME( "GUI operation is not currently supported.\n" );
+ return 0;
+ }
+ if (state->table_folder == NULL)
+ {
+ ERR( "No table folder specified (-f option).\n" );
+ show_usage();
+ return 0;
+ }
+ return 1;
+}
+
+static int process_argument( struct msidb_state *state, int i, int argc, WCHAR *argv[] )
+{
+ /* msidb accepts either "-" or "/" style flags */
+ if (strlenW(argv[i]) != 2 || (argv[i][0] != '-' && argv[i][0] != '/'))
+ {
+ WINE_FIXME( "Table names are not currently supported.\n" );
+ show_usage();
+ exit( 1 );
+ }
+ switch( argv[i][1] )
+ {
+ case '?':
+ show_usage();
+ exit( 0 );
+ case 'c':
+ state->create_database = TRUE;
+ return 1;
+ case 'd':
+ if (i + 1 >= argc) return 0;
+ state->database_file = argv[i + 1];
+ return 2;
+ case 'f':
+ if (i + 1 >= argc) return 0;
+ state->table_folder = argv[i + 1];
+ return 2;
+ default:
+ break;
+ }
+ show_usage();
+ exit( 1 );
+}
+
+static int open_database( struct msidb_state *state )
+{
+ LPCWSTR db_mode = state->create_database ? MSIDBOPEN_CREATEDIRECT : MSIDBOPEN_DIRECT;
+ UINT ret;
+
+ ret = MsiOpenDatabaseW( state->database_file, db_mode, &state->database_handle );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to open database '%s', error %d\n", wine_dbgstr_w(state->database_file), ret );
+ return 0;
+ }
+ return 1;
+}
+
+static void close_database( struct msidb_state *state )
+{
+ UINT ret;
+
+ ret = MsiDatabaseCommit( state->database_handle );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to commit changes to database.\n" );
+ return;
+ }
+ ret = MsiCloseHandle( state->database_handle );
+ if (ret != ERROR_SUCCESS)
+ {
+ WARN( "Failed to close database handle.\n" );
+ return;
+ }
+}
+
+int wmain( int argc, WCHAR *argv[] )
+{
+ struct msidb_state state;
+ int i, n = 1;
+
+ memset( &state, 0x0, sizeof(state) );
+ /* process and validate all the command line flags */
+ for (i = 1; n && i < argc; i += n)
+ n = process_argument( &state, i, argc, argv );
+ if (!valid_state( &state ))
+ return 1;
+
+ /* perform the requested operations */
+ if (!open_database( &state ))
+ {
+ ERR( "Failed to open database '%s'.\n", wine_dbgstr_w(state.database_file) );
+ return 1;
+ }
+ close_database( &state );
+ return 0;
+}
--
2.5.1

View File

@ -0,0 +1,27 @@
From fd6e6fe5c429fb1210e9ef55da0870495d066ca6 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Wed, 16 Sep 2015 13:49:46 -0600
Subject: msi: Return an error when MsiDatabaseImport is passed an invalid
pathname.
Signed-off-by: Erich E. Hoover <erich.e.hoover@wine-staging.com>
---
dlls/msi/database.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/dlls/msi/database.c b/dlls/msi/database.c
index 32e5a63..e28cf1c 100644
--- a/dlls/msi/database.c
+++ b/dlls/msi/database.c
@@ -780,6 +780,8 @@ static UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
lstrcatW( path, file );
data = msi_read_text_archive( path, &len );
+ if (data == NULL)
+ return ERROR_BAD_PATHNAME;
ptr = data;
msi_parse_line( &ptr, &columns, &num_columns, &len );
--
2.5.1

View File

@ -0,0 +1,207 @@
From 293961de18ee7e91396cb0eae1728d82b837f38f Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Wed, 16 Sep 2015 11:58:50 -0600
Subject: msidb: Add support for importing database tables.
Signed-off-by: Erich E. Hoover <erich.e.hoover@wine-staging.com>
---
programs/msidb/main.c | 113 ++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 100 insertions(+), 13 deletions(-)
diff --git a/programs/msidb/main.c b/programs/msidb/main.c
index 43cc519..c23e333 100644
--- a/programs/msidb/main.c
+++ b/programs/msidb/main.c
@@ -27,26 +27,61 @@
#include "wine/debug.h"
#include "wine/unicode.h"
+#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(msidb);
+struct msidb_listentry
+{
+ struct list entry;
+ WCHAR *name;
+};
+
struct msidb_state
{
WCHAR *database_file;
WCHAR *table_folder;
MSIHANDLE database_handle;
BOOL create_database;
+ BOOL import_tables;
+ struct list table_list;
};
+static void list_free( struct list *list )
+{
+ struct msidb_listentry *data, *next;
+
+ LIST_FOR_EACH_ENTRY_SAFE( data, next, list, struct msidb_listentry, entry )
+ {
+ list_remove( &data->entry );
+ HeapFree( GetProcessHeap(), 0, data );
+ }
+}
+
+static void list_append( struct list *list, WCHAR *name )
+{
+ struct msidb_listentry *data;
+
+ data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct msidb_listentry) );
+ if (!data)
+ {
+ ERR( "Out of memory for list.\n" );
+ exit( 1 );
+ }
+ data->name = name;
+ list_add_tail( list, &data->entry );
+}
+
static void show_usage( void )
{
WINE_MESSAGE(
- "Usage: msidb [OPTION]...[OPTION]...\n"
+ "Usage: msidb [OPTION]...[OPTION]... [TABLE]...[TABLE]\n"
"Options:\n"
" -? Show this usage message and exit.\n"
" -c Create database file (instead of opening existing file).\n"
" -d package.msi Path to the database file.\n"
" -f folder Folder in which to open/save the tables.\n"
+ " -i Import tables into database.\n"
);
}
@@ -63,6 +98,17 @@ static int valid_state( struct msidb_state *state )
show_usage();
return 0;
}
+ if (!state->create_database && !state->import_tables)
+ {
+ ERR( "No mode flag specified (-c, -i).\n" );
+ show_usage();
+ return 0;
+ }
+ if (list_empty( &state->table_list ))
+ {
+ ERR( "No tables specified.\n" );
+ return 0;
+ }
return 1;
}
@@ -70,11 +116,7 @@ static int process_argument( struct msidb_state *state, int i, int argc, WCHAR *
{
/* msidb accepts either "-" or "/" style flags */
if (strlenW(argv[i]) != 2 || (argv[i][0] != '-' && argv[i][0] != '/'))
- {
- WINE_FIXME( "Table names are not currently supported.\n" );
- show_usage();
- exit( 1 );
- }
+ return 0;
switch( argv[i][1] )
{
case '?':
@@ -91,6 +133,9 @@ static int process_argument( struct msidb_state *state, int i, int argc, WCHAR *
if (i + 1 >= argc) return 0;
state->table_folder = argv[i + 1];
return 2;
+ case 'i':
+ state->import_tables = TRUE;
+ return 1;
default:
break;
}
@@ -112,11 +157,14 @@ static int open_database( struct msidb_state *state )
return 1;
}
-static void close_database( struct msidb_state *state )
+static void close_database( struct msidb_state *state, BOOL commit_changes )
{
- UINT ret;
+ UINT ret = ERROR_SUCCESS;
- ret = MsiDatabaseCommit( state->database_handle );
+ if (state->database_handle == 0)
+ return;
+ if (commit_changes)
+ ret = MsiDatabaseCommit( state->database_handle );
if (ret != ERROR_SUCCESS)
{
ERR( "Failed to commit changes to database.\n" );
@@ -130,24 +178,63 @@ static void close_database( struct msidb_state *state )
}
}
+static int import_table( struct msidb_state *state, const WCHAR *table_name )
+{
+ const WCHAR format[] = { '%','.','8','s','.','i','d','t',0 }; /* truncate to 8 characters */
+ WCHAR table_path[MAX_PATH];
+ UINT ret;
+
+ snprintfW( table_path, sizeof(table_path)/sizeof(WCHAR), format, table_name );
+ ret = MsiDatabaseImportW( state->database_handle, state->table_folder, table_path );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to import table '%s', error %d.\n", wine_dbgstr_w(table_name), ret );
+ return 0;
+ }
+ return 1;
+}
+
+static int import_tables( struct msidb_state *state )
+{
+ struct msidb_listentry *data;
+
+ LIST_FOR_EACH_ENTRY( data, &state->table_list, struct msidb_listentry, entry )
+ {
+ if (!import_table( state, data->name ))
+ return 0; /* failed, do not commit changes */
+ }
+ return 1;
+}
+
int wmain( int argc, WCHAR *argv[] )
{
struct msidb_state state;
int i, n = 1;
+ int ret = 1;
memset( &state, 0x0, sizeof(state) );
+ list_init( &state.table_list );
/* process and validate all the command line flags */
for (i = 1; n && i < argc; i += n)
n = process_argument( &state, i, argc, argv );
+ /* process all remaining arguments as table names */
+ for (; i < argc; i++)
+ list_append( &state.table_list, argv[i] );
if (!valid_state( &state ))
- return 1;
+ goto cleanup;
/* perform the requested operations */
if (!open_database( &state ))
{
ERR( "Failed to open database '%s'.\n", wine_dbgstr_w(state.database_file) );
- return 1;
+ goto cleanup;
}
- close_database( &state );
- return 0;
+ if (state.import_tables && !import_tables( &state ))
+ goto cleanup; /* failed, do not commit changes */
+ ret = 0;
+
+cleanup:
+ close_database( &state, ret == 0 );
+ list_free( &state.table_list );
+ return ret;
}
--
2.5.1

View File

@ -0,0 +1,173 @@
From 0131987212f94916816d77e10b8825e61de64ee4 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Wed, 16 Sep 2015 14:43:18 -0600
Subject: msidb: Add support for adding stream/cabinet files to MSI databases.
Signed-off-by: Erich E. Hoover <erich.e.hoover@wine-staging.com>
---
programs/msidb/main.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 86 insertions(+), 4 deletions(-)
diff --git a/programs/msidb/main.c b/programs/msidb/main.c
index c23e333..b287484 100644
--- a/programs/msidb/main.c
+++ b/programs/msidb/main.c
@@ -42,8 +42,10 @@ struct msidb_state
WCHAR *database_file;
WCHAR *table_folder;
MSIHANDLE database_handle;
+ BOOL add_streams;
BOOL create_database;
BOOL import_tables;
+ struct list add_stream_list;
struct list table_list;
};
@@ -78,6 +80,7 @@ static void show_usage( void )
"Usage: msidb [OPTION]...[OPTION]... [TABLE]...[TABLE]\n"
"Options:\n"
" -? Show this usage message and exit.\n"
+ " -a file.cab Add stream/cabinet file to _Streams table.\n"
" -c Create database file (instead of opening existing file).\n"
" -d package.msi Path to the database file.\n"
" -f folder Folder in which to open/save the tables.\n"
@@ -92,19 +95,19 @@ static int valid_state( struct msidb_state *state )
FIXME( "GUI operation is not currently supported.\n" );
return 0;
}
- if (state->table_folder == NULL)
+ if (state->table_folder == NULL && !state->add_streams)
{
ERR( "No table folder specified (-f option).\n" );
show_usage();
return 0;
}
- if (!state->create_database && !state->import_tables)
+ if (!state->create_database && !state->import_tables && !state->add_streams)
{
- ERR( "No mode flag specified (-c, -i).\n" );
+ ERR( "No mode flag specified (-a, -c, -i).\n" );
show_usage();
return 0;
}
- if (list_empty( &state->table_list ))
+ if (list_empty( &state->table_list ) && !state->add_streams)
{
ERR( "No tables specified.\n" );
return 0;
@@ -122,6 +125,11 @@ static int process_argument( struct msidb_state *state, int i, int argc, WCHAR *
case '?':
show_usage();
exit( 0 );
+ case 'a':
+ if (i + 1 >= argc) return 0;
+ state->add_streams = TRUE;
+ list_append( &state->add_stream_list, argv[i + 1] );
+ return 2;
case 'c':
state->create_database = TRUE;
return 1;
@@ -178,6 +186,76 @@ static void close_database( struct msidb_state *state, BOOL commit_changes )
}
}
+static const WCHAR *basenameW( const WCHAR *filename )
+{
+ const WCHAR *dir_end;
+
+ dir_end = strrchrW( filename, '/' );
+ if (dir_end) return dir_end + 1;
+ dir_end = strrchrW( filename, '\\' );
+ if (dir_end) return dir_end + 1;
+ return filename;
+}
+
+static int add_stream( struct msidb_state *state, const WCHAR *stream_filename )
+{
+ static const char insert_command[] = "INSERT INTO _Streams (Name, Data) VALUES (?, ?)";
+ MSIHANDLE view = 0, record = 0;
+ UINT ret;
+
+ ret = MsiDatabaseOpenViewA( state->database_handle, insert_command, &view );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to open _Streams table.\n" );
+ goto cleanup;
+ }
+ record = MsiCreateRecord( 2 );
+ if (record == 0)
+ {
+ ERR( "Failed to create MSI record.\n" );
+ ret = ERROR_OUTOFMEMORY;
+ goto cleanup;
+ }
+ ret = MsiRecordSetStringW( record, 1, basenameW( stream_filename ) );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to add stream filename to MSI record.\n" );
+ goto cleanup;
+ }
+ ret = MsiRecordSetStreamW( record, 2, stream_filename );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to add stream to MSI record.\n" );
+ goto cleanup;
+ }
+ ret = MsiViewExecute( view, record );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to add stream to _Streams table.\n" );
+ goto cleanup;
+ }
+
+cleanup:
+ if (record)
+ MsiCloseHandle( record );
+ if (view)
+ MsiViewClose( view );
+
+ return (ret == ERROR_SUCCESS);
+}
+
+static int add_streams( struct msidb_state *state )
+{
+ struct msidb_listentry *data;
+
+ LIST_FOR_EACH_ENTRY( data, &state->add_stream_list, struct msidb_listentry, entry )
+ {
+ if (!add_stream( state, data->name ))
+ return 0; /* failed, do not commit changes */
+ }
+ return 1;
+}
+
static int import_table( struct msidb_state *state, const WCHAR *table_name )
{
const WCHAR format[] = { '%','.','8','s','.','i','d','t',0 }; /* truncate to 8 characters */
@@ -213,6 +291,7 @@ int wmain( int argc, WCHAR *argv[] )
int ret = 1;
memset( &state, 0x0, sizeof(state) );
+ list_init( &state.add_stream_list );
list_init( &state.table_list );
/* process and validate all the command line flags */
for (i = 1; n && i < argc; i += n)
@@ -229,12 +308,15 @@ int wmain( int argc, WCHAR *argv[] )
ERR( "Failed to open database '%s'.\n", wine_dbgstr_w(state.database_file) );
goto cleanup;
}
+ if (state.add_streams && !add_streams( &state ))
+ goto cleanup; /* failed, do not commit changes */
if (state.import_tables && !import_tables( &state ))
goto cleanup; /* failed, do not commit changes */
ret = 0;
cleanup:
close_database( &state, ret == 0 );
+ list_free( &state.add_stream_list );
list_free( &state.table_list );
return ret;
}
--
2.5.1

View File

@ -0,0 +1,64 @@
From 06bd88e4154eb5eb41011a3f51fe7fac05447b41 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Thu, 17 Sep 2015 10:21:27 -0600
Subject: msi: Add support for deleting streams from an MSI database.
Signed-off-by: Erich E. Hoover <erich.e.hoover@wine-staging.com>
---
dlls/msi/streams.c | 28 ++++++++++++++++++++++++++--
1 file changed, 26 insertions(+), 2 deletions(-)
diff --git a/dlls/msi/streams.c b/dlls/msi/streams.c
index 7f9582c..e170c03 100644
--- a/dlls/msi/streams.c
+++ b/dlls/msi/streams.c
@@ -208,7 +208,28 @@ static UINT STREAMS_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row
static UINT STREAMS_delete_row(struct tagMSIVIEW *view, UINT row)
{
- FIXME("(%p %d): stub!\n", view, row);
+ MSIDATABASE *db = ((MSISTREAMSVIEW *)view)->db;
+ UINT i, num_rows = db->num_streams - 1;
+ const WCHAR *name;
+ WCHAR *encname;
+ HRESULT hr;
+
+ TRACE("(%p %d)!\n", view, row);
+
+ name = msi_string_lookup( db->strings, db->streams[row].str_index, NULL );
+ if (!(encname = encode_streamname( FALSE, name ))) return ERROR_OUTOFMEMORY;
+ hr = IStorage_DestroyElement( db->storage, encname );
+ msi_free( encname );
+ if (FAILED( hr ))
+ return ERROR_FUNCTION_FAILED;
+ hr = IStream_Release( db->streams[row].stream );
+ if (FAILED( hr ))
+ return ERROR_FUNCTION_FAILED;
+
+ for (i = row; i < num_rows; i++)
+ db->streams[i] = db->streams[i + 1];
+ db->num_streams = num_rows;
+
return ERROR_SUCCESS;
}
@@ -307,12 +328,15 @@ static UINT STREAMS_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRE
r = streams_modify_update(view, rec);
break;
+ case MSIMODIFY_DELETE:
+ r = STREAMS_delete_row(view, row - 1);
+ break;
+
case MSIMODIFY_VALIDATE_NEW:
case MSIMODIFY_INSERT_TEMPORARY:
case MSIMODIFY_REFRESH:
case MSIMODIFY_REPLACE:
case MSIMODIFY_MERGE:
- case MSIMODIFY_DELETE:
case MSIMODIFY_VALIDATE:
case MSIMODIFY_VALIDATE_FIELD:
case MSIMODIFY_VALIDATE_DELETE:
--
2.5.1

View File

@ -0,0 +1,158 @@
From 1a79601491aa1a2ccdff262df085a4c025d4e724 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Thu, 17 Sep 2015 10:32:50 -0600
Subject: msidb: Add support for removing stream/cabinet files from MSI
databases.
Signed-off-by: Erich E. Hoover <erich.e.hoover@wine-staging.com>
---
programs/msidb/main.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 70 insertions(+), 4 deletions(-)
diff --git a/programs/msidb/main.c b/programs/msidb/main.c
index b287484..51b289c 100644
--- a/programs/msidb/main.c
+++ b/programs/msidb/main.c
@@ -43,9 +43,11 @@ struct msidb_state
WCHAR *table_folder;
MSIHANDLE database_handle;
BOOL add_streams;
+ BOOL kill_streams;
BOOL create_database;
BOOL import_tables;
struct list add_stream_list;
+ struct list kill_stream_list;
struct list table_list;
};
@@ -85,6 +87,7 @@ static void show_usage( void )
" -d package.msi Path to the database file.\n"
" -f folder Folder in which to open/save the tables.\n"
" -i Import tables into database.\n"
+ " -k file.cab Kill (remove) stream/cabinet file from _Streams table.\n"
);
}
@@ -95,19 +98,20 @@ static int valid_state( struct msidb_state *state )
FIXME( "GUI operation is not currently supported.\n" );
return 0;
}
- if (state->table_folder == NULL && !state->add_streams)
+ if (state->table_folder == NULL && !state->add_streams && !state->kill_streams)
{
ERR( "No table folder specified (-f option).\n" );
show_usage();
return 0;
}
- if (!state->create_database && !state->import_tables && !state->add_streams)
+ if (!state->create_database && !state->import_tables && !state->add_streams
+ && !state->kill_streams)
{
- ERR( "No mode flag specified (-a, -c, -i).\n" );
+ ERR( "No mode flag specified (-a, -c, -i, -k).\n" );
show_usage();
return 0;
}
- if (list_empty( &state->table_list ) && !state->add_streams)
+ if (list_empty( &state->table_list ) && !state->add_streams && !state->kill_streams)
{
ERR( "No tables specified.\n" );
return 0;
@@ -144,6 +148,11 @@ static int process_argument( struct msidb_state *state, int i, int argc, WCHAR *
case 'i':
state->import_tables = TRUE;
return 1;
+ case 'k':
+ if (i + 1 >= argc) return 0;
+ state->kill_streams = TRUE;
+ list_append( &state->kill_stream_list, argv[i + 1] );
+ return 2;
default:
break;
}
@@ -256,6 +265,59 @@ static int add_streams( struct msidb_state *state )
return 1;
}
+static int kill_stream( struct msidb_state *state, const WCHAR *stream_filename )
+{
+ static const char delete_command[] = "DELETE FROM _Streams WHERE Name = ?";
+ MSIHANDLE view = 0, record = 0;
+ UINT ret;
+
+ ret = MsiDatabaseOpenViewA( state->database_handle, delete_command, &view );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to open _Streams table.\n" );
+ goto cleanup;
+ }
+ record = MsiCreateRecord( 1 );
+ if (record == 0)
+ {
+ ERR( "Failed to create MSI record.\n" );
+ ret = ERROR_OUTOFMEMORY;
+ goto cleanup;
+ }
+ ret = MsiRecordSetStringW( record, 1, stream_filename );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to add stream filename to MSI record.\n" );
+ goto cleanup;
+ }
+ ret = MsiViewExecute( view, record );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to delete stream from _Streams table.\n" );
+ goto cleanup;
+ }
+
+cleanup:
+ if (record)
+ MsiCloseHandle( record );
+ if (view)
+ MsiViewClose( view );
+
+ return (ret == ERROR_SUCCESS);
+}
+
+static int kill_streams( struct msidb_state *state )
+{
+ struct msidb_listentry *data;
+
+ LIST_FOR_EACH_ENTRY( data, &state->kill_stream_list, struct msidb_listentry, entry )
+ {
+ if (!kill_stream( state, data->name ))
+ return 0; /* failed, do not commit changes */
+ }
+ return 1;
+}
+
static int import_table( struct msidb_state *state, const WCHAR *table_name )
{
const WCHAR format[] = { '%','.','8','s','.','i','d','t',0 }; /* truncate to 8 characters */
@@ -292,6 +354,7 @@ int wmain( int argc, WCHAR *argv[] )
memset( &state, 0x0, sizeof(state) );
list_init( &state.add_stream_list );
+ list_init( &state.kill_stream_list );
list_init( &state.table_list );
/* process and validate all the command line flags */
for (i = 1; n && i < argc; i += n)
@@ -312,11 +375,14 @@ int wmain( int argc, WCHAR *argv[] )
goto cleanup; /* failed, do not commit changes */
if (state.import_tables && !import_tables( &state ))
goto cleanup; /* failed, do not commit changes */
+ if (state.kill_streams && !kill_streams( &state ))
+ goto cleanup; /* failed, do not commit changes */
ret = 0;
cleanup:
close_database( &state, ret == 0 );
list_free( &state.add_stream_list );
+ list_free( &state.kill_stream_list );
list_free( &state.table_list );
return ret;
}
--
2.5.1

View File

@ -0,0 +1,199 @@
From 683cf39b0e8ff7e497900fdd52d84d50bec0b3a2 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Thu, 17 Sep 2015 14:04:17 -0600
Subject: msidb: Add support for extracting stream/cabinet files from MSI
databases.
Signed-off-by: Erich E. Hoover <erich.e.hoover@wine-staging.com>
---
programs/msidb/main.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 107 insertions(+), 4 deletions(-)
diff --git a/programs/msidb/main.c b/programs/msidb/main.c
index 51b289c..ef63665 100644
--- a/programs/msidb/main.c
+++ b/programs/msidb/main.c
@@ -43,10 +43,12 @@ struct msidb_state
WCHAR *table_folder;
MSIHANDLE database_handle;
BOOL add_streams;
+ BOOL extract_streams;
BOOL kill_streams;
BOOL create_database;
BOOL import_tables;
struct list add_stream_list;
+ struct list extract_stream_list;
struct list kill_stream_list;
struct list table_list;
};
@@ -88,6 +90,7 @@ static void show_usage( void )
" -f folder Folder in which to open/save the tables.\n"
" -i Import tables into database.\n"
" -k file.cab Kill (remove) stream/cabinet file from _Streams table.\n"
+ " -x file.cab Extract stream/cabinet file from _Streams table.\n"
);
}
@@ -98,20 +101,22 @@ static int valid_state( struct msidb_state *state )
FIXME( "GUI operation is not currently supported.\n" );
return 0;
}
- if (state->table_folder == NULL && !state->add_streams && !state->kill_streams)
+ if (state->table_folder == NULL && !state->add_streams && !state->kill_streams
+ && !state->extract_streams)
{
ERR( "No table folder specified (-f option).\n" );
show_usage();
return 0;
}
if (!state->create_database && !state->import_tables && !state->add_streams
- && !state->kill_streams)
+ && !state->kill_streams && !state->extract_streams)
{
- ERR( "No mode flag specified (-a, -c, -i, -k).\n" );
+ ERR( "No mode flag specified (-a, -c, -i, -k, -x).\n" );
show_usage();
return 0;
}
- if (list_empty( &state->table_list ) && !state->add_streams && !state->kill_streams)
+ if (list_empty( &state->table_list ) && !state->add_streams && !state->kill_streams
+ && !state->extract_streams)
{
ERR( "No tables specified.\n" );
return 0;
@@ -153,6 +158,11 @@ static int process_argument( struct msidb_state *state, int i, int argc, WCHAR *
state->kill_streams = TRUE;
list_append( &state->kill_stream_list, argv[i + 1] );
return 2;
+ case 'x':
+ if (i + 1 >= argc) return 0;
+ state->extract_streams = TRUE;
+ list_append( &state->extract_stream_list, argv[i + 1] );
+ return 2;
default:
break;
}
@@ -318,6 +328,95 @@ static int kill_streams( struct msidb_state *state )
return 1;
}
+static int extract_stream( struct msidb_state *state, const WCHAR *stream_filename )
+{
+ static const char select_command[] = "SELECT Data FROM _Streams WHERE Name = ?";
+ HANDLE file = INVALID_HANDLE_VALUE;
+ MSIHANDLE view = 0, record = 0;
+ DWORD read_size, write_size;
+ char buffer[1024];
+ UINT ret;
+
+ file = CreateFileW( stream_filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
+ if (file == INVALID_HANDLE_VALUE)
+ {
+ ret = ERROR_FILE_NOT_FOUND;
+ ERR( "Failed to open destination file %s.\n", wine_dbgstr_w(stream_filename) );
+ goto cleanup;
+ }
+ ret = MsiDatabaseOpenViewA( state->database_handle, select_command, &view );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to open _Streams table.\n" );
+ goto cleanup;
+ }
+ record = MsiCreateRecord( 1 );
+ if (record == 0)
+ {
+ ERR( "Failed to create MSI record.\n" );
+ ret = ERROR_OUTOFMEMORY;
+ goto cleanup;
+ }
+ ret = MsiRecordSetStringW( record, 1, stream_filename );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to add stream filename to MSI record.\n" );
+ goto cleanup;
+ }
+ ret = MsiViewExecute( view, record );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to query stream from _Streams table.\n" );
+ goto cleanup;
+ }
+ MsiCloseHandle( record );
+ record = 0;
+ ret = MsiViewFetch( view, &record );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to query row from _Streams table.\n" );
+ goto cleanup;
+ }
+ read_size = sizeof(buffer);
+ while (read_size == sizeof(buffer))
+ {
+ ret = MsiRecordReadStream( record, 1, buffer, &read_size );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to read stream from _Streams table.\n" );
+ goto cleanup;
+ }
+ if (!WriteFile( file, buffer, read_size, &write_size, NULL ) || read_size != write_size)
+ {
+ ret = ERROR_WRITE_FAULT;
+ ERR( "Failed to write stream to destination file.\n" );
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ if (record)
+ MsiCloseHandle( record );
+ if (view)
+ MsiViewClose( view );
+ if (file != INVALID_HANDLE_VALUE)
+ CloseHandle( file );
+ return (ret == ERROR_SUCCESS);
+}
+
+static int extract_streams( struct msidb_state *state )
+{
+ struct msidb_listentry *data;
+
+ LIST_FOR_EACH_ENTRY( data, &state->extract_stream_list, struct msidb_listentry, entry )
+ {
+ if (!extract_stream( state, data->name ))
+ return 0; /* failed, do not commit changes */
+ }
+ return 1;
+}
+
static int import_table( struct msidb_state *state, const WCHAR *table_name )
{
const WCHAR format[] = { '%','.','8','s','.','i','d','t',0 }; /* truncate to 8 characters */
@@ -354,6 +453,7 @@ int wmain( int argc, WCHAR *argv[] )
memset( &state, 0x0, sizeof(state) );
list_init( &state.add_stream_list );
+ list_init( &state.extract_stream_list );
list_init( &state.kill_stream_list );
list_init( &state.table_list );
/* process and validate all the command line flags */
@@ -373,6 +473,8 @@ int wmain( int argc, WCHAR *argv[] )
}
if (state.add_streams && !add_streams( &state ))
goto cleanup; /* failed, do not commit changes */
+ if (state.extract_streams && !extract_streams( &state ))
+ goto cleanup; /* failed, do not commit changes */
if (state.import_tables && !import_tables( &state ))
goto cleanup; /* failed, do not commit changes */
if (state.kill_streams && !kill_streams( &state ))
@@ -382,6 +484,7 @@ int wmain( int argc, WCHAR *argv[] )
cleanup:
close_database( &state, ret == 0 );
list_free( &state.add_stream_list );
+ list_free( &state.extract_stream_list );
list_free( &state.kill_stream_list );
list_free( &state.table_list );
return ret;
--
2.5.1

View File

@ -0,0 +1,101 @@
From fa147d70dd017074fb40361ac4911f09d4c7a8c2 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Thu, 17 Sep 2015 15:04:17 -0600
Subject: msidb: Add support for exporting database tables.
Signed-off-by: Erich E. Hoover <erich.e.hoover@wine-staging.com>
---
programs/msidb/main.c | 41 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 38 insertions(+), 3 deletions(-)
diff --git a/programs/msidb/main.c b/programs/msidb/main.c
index ef63665..7eb6d8b 100644
--- a/programs/msidb/main.c
+++ b/programs/msidb/main.c
@@ -47,6 +47,7 @@ struct msidb_state
BOOL kill_streams;
BOOL create_database;
BOOL import_tables;
+ BOOL export_tables;
struct list add_stream_list;
struct list extract_stream_list;
struct list kill_stream_list;
@@ -87,6 +88,7 @@ static void show_usage( void )
" -a file.cab Add stream/cabinet file to _Streams table.\n"
" -c Create database file (instead of opening existing file).\n"
" -d package.msi Path to the database file.\n"
+ " -e Export tables from database.\n"
" -f folder Folder in which to open/save the tables.\n"
" -i Import tables into database.\n"
" -k file.cab Kill (remove) stream/cabinet file from _Streams table.\n"
@@ -108,10 +110,10 @@ static int valid_state( struct msidb_state *state )
show_usage();
return 0;
}
- if (!state->create_database && !state->import_tables && !state->add_streams
- && !state->kill_streams && !state->extract_streams)
+ if (!state->create_database && !state->import_tables && !state->export_tables
+ && !state->add_streams&& !state->kill_streams && !state->extract_streams)
{
- ERR( "No mode flag specified (-a, -c, -i, -k, -x).\n" );
+ ERR( "No mode flag specified (-a, -c, -e, -i, -k, -x).\n" );
show_usage();
return 0;
}
@@ -146,6 +148,9 @@ static int process_argument( struct msidb_state *state, int i, int argc, WCHAR *
if (i + 1 >= argc) return 0;
state->database_file = argv[i + 1];
return 2;
+ case 'e':
+ state->export_tables = TRUE;
+ return 1;
case 'f':
if (i + 1 >= argc) return 0;
state->table_folder = argv[i + 1];
@@ -445,6 +450,34 @@ static int import_tables( struct msidb_state *state )
return 1;
}
+static int export_table( struct msidb_state *state, const WCHAR *table_name )
+{
+ const WCHAR format[] = { '%','s','.','i','d','t',0 };
+ WCHAR table_path[MAX_PATH];
+ UINT ret;
+
+ snprintfW( table_path, sizeof(table_path)/sizeof(WCHAR), format, table_name );
+ ret = MsiDatabaseExportW( state->database_handle, table_name, state->table_folder, table_path );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to export table '%s', error %d.\n", wine_dbgstr_w(table_name), ret );
+ return 0;
+ }
+ return 1;
+}
+
+static int export_tables( struct msidb_state *state )
+{
+ struct msidb_listentry *data;
+
+ LIST_FOR_EACH_ENTRY( data, &state->table_list, struct msidb_listentry, entry )
+ {
+ if (!export_table( state, data->name ))
+ return 0; /* failed, do not commit changes */
+ }
+ return 1;
+}
+
int wmain( int argc, WCHAR *argv[] )
{
struct msidb_state state;
@@ -473,6 +506,8 @@ int wmain( int argc, WCHAR *argv[] )
}
if (state.add_streams && !add_streams( &state ))
goto cleanup; /* failed, do not commit changes */
+ if (state.export_tables && !export_tables( &state ))
+ goto cleanup; /* failed, do not commit changes */
if (state.extract_streams && !extract_streams( &state ))
goto cleanup; /* failed, do not commit changes */
if (state.import_tables && !import_tables( &state ))
--
2.5.1

View File

@ -0,0 +1,54 @@
From 767e70ef58c963eef742ee95b64c4c488035c3e7 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Thu, 17 Sep 2015 15:09:24 -0600
Subject: msidb: Add support for exporting with short (DOS) filenames.
Signed-off-by: Erich E. Hoover <erich.e.hoover@wine-staging.com>
---
programs/msidb/main.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/programs/msidb/main.c b/programs/msidb/main.c
index 7eb6d8b..49dd11b 100644
--- a/programs/msidb/main.c
+++ b/programs/msidb/main.c
@@ -48,6 +48,7 @@ struct msidb_state
BOOL create_database;
BOOL import_tables;
BOOL export_tables;
+ BOOL short_filenames;
struct list add_stream_list;
struct list extract_stream_list;
struct list kill_stream_list;
@@ -92,6 +93,7 @@ static void show_usage( void )
" -f folder Folder in which to open/save the tables.\n"
" -i Import tables into database.\n"
" -k file.cab Kill (remove) stream/cabinet file from _Streams table.\n"
+ " -s Export with short filenames (eight character max).\n"
" -x file.cab Extract stream/cabinet file from _Streams table.\n"
);
}
@@ -163,6 +165,9 @@ static int process_argument( struct msidb_state *state, int i, int argc, WCHAR *
state->kill_streams = TRUE;
list_append( &state->kill_stream_list, argv[i + 1] );
return 2;
+ case 's':
+ state->short_filenames = TRUE;
+ return 1;
case 'x':
if (i + 1 >= argc) return 0;
state->extract_streams = TRUE;
@@ -452,7 +457,9 @@ static int import_tables( struct msidb_state *state )
static int export_table( struct msidb_state *state, const WCHAR *table_name )
{
- const WCHAR format[] = { '%','s','.','i','d','t',0 };
+ const WCHAR format_dos[] = { '%','.','8','s','.','i','d','t',0 }; /* truncate to 8 characters */
+ const WCHAR format_full[] = { '%','s','.','i','d','t',0 };
+ const WCHAR *format = (state->short_filenames ? format_dos : format_full);
WCHAR table_path[MAX_PATH];
UINT ret;
--
2.5.1

View File

@ -0,0 +1,200 @@
From 65d3fe2e1b64341598f8e427f2e331e430617aac Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Thu, 17 Sep 2015 17:56:15 -0600
Subject: msi: Add support for exporting the _SummaryInformation table.
Signed-off-by: Erich E. Hoover <erich.e.hoover@wine-staging.com>
---
dlls/msi/database.c | 22 +++++++++++
dlls/msi/msipriv.h | 1 +
dlls/msi/suminfo.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 135 insertions(+)
diff --git a/dlls/msi/database.c b/dlls/msi/database.c
index e28cf1c..13eac2a 100644
--- a/dlls/msi/database.c
+++ b/dlls/msi/database.c
@@ -987,9 +987,25 @@ static UINT msi_export_forcecodepage( HANDLE handle, UINT codepage )
return ERROR_SUCCESS;
}
+static UINT msi_export_summaryinformation( MSIDATABASE *db, HANDLE handle )
+{
+ static const char header[] = "PropertyId\tValue\r\n"
+ "i2\tl255\r\n"
+ "_SummaryInformation\tPropertyId\r\n";
+ DWORD sz;
+
+ sz = lstrlenA(header);
+ if (!WriteFile(handle, header, sz, &sz, NULL))
+ return ERROR_WRITE_FAULT;
+
+ return msi_export_suminfo( db, handle );
+}
+
static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
LPCWSTR folder, LPCWSTR file )
{
+ static const WCHAR summaryinformation[] = {
+ '_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
static const WCHAR query[] = {
's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
static const WCHAR forcecodepage[] = {
@@ -1028,6 +1044,12 @@ static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
goto done;
}
+ if (!strcmpW( table, summaryinformation ))
+ {
+ r = msi_export_summaryinformation( db, handle );
+ goto done;
+ }
+
r = MSI_OpenQuery( db, &view, query, table );
if (r == ERROR_SUCCESS)
{
diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h
index d457a54..71b620c 100644
--- a/dlls/msi/msipriv.h
+++ b/dlls/msi/msipriv.h
@@ -954,6 +954,7 @@ extern LPWSTR msi_suminfo_dup_string( MSISUMMARYINFO *si, UINT uiProperty ) DECL
extern INT msi_suminfo_get_int32( MSISUMMARYINFO *si, UINT uiProperty ) DECLSPEC_HIDDEN;
extern LPWSTR msi_get_suminfo_product( IStorage *stg ) DECLSPEC_HIDDEN;
extern UINT msi_add_suminfo( MSIDATABASE *db, LPWSTR **records, int num_records, int num_columns ) DECLSPEC_HIDDEN;
+extern UINT msi_export_suminfo( MSIDATABASE *db, HANDLE handle ) DECLSPEC_HIDDEN;
extern enum platform parse_platform( const WCHAR *str ) DECLSPEC_HIDDEN;
extern UINT msi_load_suminfo_properties( MSIPACKAGE *package ) DECLSPEC_HIDDEN;
diff --git a/dlls/msi/suminfo.c b/dlls/msi/suminfo.c
index 451fd16..c929fa5 100644
--- a/dlls/msi/suminfo.c
+++ b/dlls/msi/suminfo.c
@@ -23,6 +23,7 @@
#define COBJMACROS
#define NONAMELESSUNION
+#include "stdio.h"
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
@@ -1015,6 +1016,117 @@ end:
return r;
}
+static UINT save_prop( MSISUMMARYINFO *si, HANDLE handle, UINT row )
+{
+ static const char fmt_systemtime[] = "%d/%02d/%02d %02d:%02d:%02d";
+ char data[20]; /* largest string: YYYY/MM/DD hh:mm:ss */
+ static const char fmt_begin[] = "%u\t";
+ static const char data_end[] = "\r\n";
+ static const char fmt_int[] = "%u";
+ UINT r, data_type, len;
+ SYSTEMTIME system_time;
+ FILETIME file_time;
+ INT int_value;
+ awstring str;
+ DWORD sz;
+
+ str.unicode = FALSE;
+ str.str.a = NULL;
+ len = 0;
+ r = get_prop( si, row, &data_type, &int_value, &file_time, &str, &len );
+ if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA)
+ return r;
+ if (data_type == VT_EMPTY)
+ return ERROR_SUCCESS; /* property not set */
+ snprintf( data, sizeof(data), fmt_begin, row );
+ sz = lstrlenA( data );
+ if (!WriteFile( handle, data, sz, &sz, NULL ))
+ return ERROR_WRITE_FAULT;
+
+ switch (data_type)
+ {
+ case VT_I2:
+ case VT_I4:
+ snprintf( data, sizeof(data), fmt_int, int_value );
+ sz = lstrlenA( data );
+ if (!WriteFile( handle, data, sz, &sz, NULL ))
+ return ERROR_WRITE_FAULT;
+ break;
+ case VT_LPSTR:
+ len++;
+ if (!(str.str.a = msi_alloc( len )))
+ return ERROR_OUTOFMEMORY;
+ r = get_prop( si, row, NULL, NULL, NULL, &str, &len );
+ if (r != ERROR_SUCCESS)
+ {
+ msi_free( str.str.a );
+ return r;
+ }
+ sz = lstrlenA( str.str.a );
+ if (!WriteFile( handle, str.str.a, sz, &sz, NULL ))
+ {
+ msi_free( str.str.a );
+ return ERROR_WRITE_FAULT;
+ }
+ msi_free( str.str.a );
+ break;
+ case VT_FILETIME:
+ if (!FileTimeToSystemTime( &file_time, &system_time ))
+ return ERROR_FUNCTION_FAILED;
+ snprintf( data, sizeof(data), fmt_systemtime, system_time.wYear, system_time.wMonth,
+ system_time.wDay, system_time.wHour, system_time.wMinute,
+ system_time.wSecond );
+ sz = lstrlenA( data );
+ if (!WriteFile( handle, data, sz, &sz, NULL ))
+ return ERROR_WRITE_FAULT;
+ break;
+ case VT_EMPTY:
+ /* cannot reach here, property not set */
+ break;
+ default:
+ FIXME( "Unknown property variant type\n" );
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ sz = lstrlenA( data_end );
+ if (!WriteFile( handle, data_end, sz, &sz, NULL ))
+ return ERROR_WRITE_FAULT;
+
+ return ERROR_SUCCESS;
+}
+
+UINT msi_export_suminfo( MSIDATABASE *db, HANDLE handle )
+{
+ UINT i, r, num_rows;
+ MSISUMMARYINFO *si;
+
+ r = msi_get_suminfo( db->storage, 0, &si );
+ if (r != ERROR_SUCCESS)
+ r = msi_get_db_suminfo( db, 0, &si );
+ if (r != ERROR_SUCCESS)
+ return r;
+
+ num_rows = get_property_count( si->property );
+ if (!num_rows)
+ {
+ msiobj_release( &si->hdr );
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ for (i = 0; i < num_rows; i++)
+ {
+ r = save_prop( si, handle, i );
+ if (r != ERROR_SUCCESS)
+ {
+ msiobj_release( &si->hdr );
+ return r;
+ }
+ }
+
+ msiobj_release( &si->hdr );
+ return ERROR_SUCCESS;
+}
+
UINT WINAPI MsiSummaryInfoPersist( MSIHANDLE handle )
{
MSISUMMARYINFO *si;
--
2.5.1

View File

@ -0,0 +1,113 @@
From 95dff4faee45b610f212b57cf526bee256b7b4ff Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Fri, 18 Sep 2015 10:15:20 -0600
Subject: msi: Break out field exporting into a separate routine.
Signed-off-by: Erich E. Hoover <erich.e.hoover@wine-staging.com>
---
dlls/msi/database.c | 73 +++++++++++++++++++++++++++++++++--------------------
1 file changed, 45 insertions(+), 28 deletions(-)
diff --git a/dlls/msi/database.c b/dlls/msi/database.c
index 13eac2a..7c511c9 100644
--- a/dlls/msi/database.c
+++ b/dlls/msi/database.c
@@ -920,50 +920,67 @@ end:
return r;
}
-static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
+static UINT msi_export_field( HANDLE handle, MSIRECORD *row, UINT field )
{
- UINT i, count, len, r = ERROR_SUCCESS;
- const char *sep;
char *buffer;
+ BOOL bret;
DWORD sz;
+ UINT r;
- len = 0x100;
- buffer = msi_alloc( len );
- if ( !buffer )
+ sz = 0x100;
+ buffer = msi_alloc( sz );
+ if (!buffer)
return ERROR_OUTOFMEMORY;
- count = MSI_RecordGetFieldCount( row );
- for ( i=start; i<=count; i++ )
+ r = MSI_RecordGetStringA( row, field, buffer, &sz );
+ if (r == ERROR_MORE_DATA)
{
- sz = len;
- r = MSI_RecordGetStringA( row, i, buffer, &sz );
- if (r == ERROR_MORE_DATA)
+ char *p;
+
+ sz++; /* leave room for NULL terminator */
+ p = msi_realloc( buffer, sz );
+ if (!p)
{
- char *p = msi_realloc( buffer, sz + 1 );
- if (!p)
- break;
- len = sz + 1;
- buffer = p;
+ msi_free( buffer );
+ return ERROR_OUTOFMEMORY;
}
- sz = len;
- r = MSI_RecordGetStringA( row, i, buffer, &sz );
- if (r != ERROR_SUCCESS)
- break;
+ buffer = p;
- if (!WriteFile( handle, buffer, sz, &sz, NULL ))
+ r = MSI_RecordGetStringA( row, field, buffer, &sz );
+ if (r != ERROR_SUCCESS)
{
- r = ERROR_FUNCTION_FAILED;
- break;
+ msi_free( buffer );
+ return r;
}
+ }
+ else if (r != ERROR_SUCCESS)
+ return r;
+
+ bret = WriteFile( handle, buffer, sz, &sz, NULL );
+ msi_free( buffer );
+ if (!bret)
+ return ERROR_FUNCTION_FAILED;
+
+ return r;
+}
+
+static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
+{
+ UINT i, count, r = ERROR_SUCCESS;
+ const char *sep;
+ DWORD sz;
+
+ count = MSI_RecordGetFieldCount( row );
+ for (i = start; i <= count; i++)
+ {
+ r = msi_export_field( handle, row, i );
+ if (r != ERROR_SUCCESS)
+ return r;
sep = (i < count) ? "\t" : "\r\n";
if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
- {
- r = ERROR_FUNCTION_FAILED;
- break;
- }
+ return ERROR_FUNCTION_FAILED;
}
- msi_free( buffer );
return r;
}
--
2.5.1

View File

@ -0,0 +1,153 @@
From fdb59c20c2b04b0c5c405b0926f785efaaa2b174 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Fri, 18 Sep 2015 11:07:43 -0600
Subject: msi: Add support for exporting binary streams (Binary/Icon tables).
Signed-off-by: Erich E. Hoover <erich.e.hoover@wine-staging.com>
---
dlls/msi/database.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 79 insertions(+), 6 deletions(-)
diff --git a/dlls/msi/database.c b/dlls/msi/database.c
index 7c511c9..464b2d4 100644
--- a/dlls/msi/database.c
+++ b/dlls/msi/database.c
@@ -53,6 +53,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(msi);
#define IS_INTMSIDBOPEN(x) (((ULONG_PTR)(x) >> 16) == 0)
+struct row_export_info
+{
+ HANDLE handle;
+ LPCWSTR folder;
+ LPCWSTR table;
+};
+
static void free_transforms( MSIDATABASE *db )
{
while( !list_empty( &db->transforms ) )
@@ -964,8 +971,61 @@ static UINT msi_export_field( HANDLE handle, MSIRECORD *row, UINT field )
return r;
}
-static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
+static UINT msi_export_stream( LPCWSTR folder, LPCWSTR table, MSIRECORD *row, UINT field,
+ UINT start )
{
+ static const WCHAR fmt_file[] = { '%','s','/','%','s','/','%','s',0 };
+ static const WCHAR fmt_folder[] = { '%','s','/','%','s',0 };
+ WCHAR stream_name[256], stream_filename[MAX_PATH];
+ DWORD sz, read_size, write_size;
+ char buffer[1024];
+ HANDLE file;
+ UINT r;
+
+ /* get the name of the file */
+ sz = sizeof(stream_name)/sizeof(WCHAR);
+ r = MSI_RecordGetStringW( row, start, stream_name, &sz );
+ if (r != ERROR_SUCCESS)
+ return r;
+
+ /* if the destination folder does not exist then create it (folder name = table name) */
+ snprintfW( stream_filename, sizeof(stream_filename), fmt_folder, folder, table );
+ if (GetFileAttributesW( stream_filename ) == INVALID_FILE_ATTRIBUTES)
+ {
+ if (!CreateDirectoryW( stream_filename, NULL ))
+ return ERROR_PATH_NOT_FOUND;
+ }
+
+ /* actually create the file */
+ snprintfW( stream_filename, sizeof(stream_filename), fmt_file, folder, table, stream_name );
+ file = CreateFileW( stream_filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
+ if (file == INVALID_HANDLE_VALUE)
+ return ERROR_FILE_NOT_FOUND;
+
+ /* copy the stream to the file */
+ read_size = sizeof(buffer);
+ while (read_size == sizeof(buffer))
+ {
+ r = MSI_RecordReadStream( row, field, buffer, &read_size );
+ if (r != ERROR_SUCCESS)
+ {
+ CloseHandle( file );
+ return r;
+ }
+ if (!WriteFile( file, buffer, read_size, &write_size, NULL ) || read_size != write_size)
+ {
+ CloseHandle( file );
+ return ERROR_WRITE_FAULT;
+ }
+ }
+ CloseHandle( file );
+ return r;
+}
+
+static UINT msi_export_record( struct row_export_info *row_export_info, MSIRECORD *row, UINT start )
+{
+ HANDLE handle = row_export_info->handle;
UINT i, count, r = ERROR_SUCCESS;
const char *sep;
DWORD sz;
@@ -974,7 +1034,18 @@ static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
for (i = start; i <= count; i++)
{
r = msi_export_field( handle, row, i );
- if (r != ERROR_SUCCESS)
+ if (r == ERROR_INVALID_PARAMETER)
+ {
+ r = msi_export_stream( row_export_info->folder, row_export_info->table, row, i, start );
+ if (r != ERROR_SUCCESS)
+ return r;
+
+ /* exporting a binary stream, repeat the "Name" field */
+ r = msi_export_field( handle, row, start );
+ if (r != ERROR_SUCCESS)
+ return r;
+ }
+ else if (r != ERROR_SUCCESS)
return r;
sep = (i < count) ? "\t" : "\r\n";
@@ -1070,11 +1141,13 @@ static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
r = MSI_OpenQuery( db, &view, query, table );
if (r == ERROR_SUCCESS)
{
+ struct row_export_info row_export_info = { handle, folder, table };
+
/* write out row 1, the column names */
r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
if (r == ERROR_SUCCESS)
{
- msi_export_record( handle, rec, 1 );
+ msi_export_record( &row_export_info, rec, 1 );
msiobj_release( &rec->hdr );
}
@@ -1082,7 +1155,7 @@ static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
if (r == ERROR_SUCCESS)
{
- msi_export_record( handle, rec, 1 );
+ msi_export_record( &row_export_info, rec, 1 );
msiobj_release( &rec->hdr );
}
@@ -1091,12 +1164,12 @@ static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
if (r == ERROR_SUCCESS)
{
MSI_RecordSetStringW( rec, 0, table );
- msi_export_record( handle, rec, 0 );
+ msi_export_record( &row_export_info, rec, 0 );
msiobj_release( &rec->hdr );
}
/* write out row 4 onwards, the data */
- r = MSI_IterateRecords( view, 0, msi_export_row, handle );
+ r = MSI_IterateRecords( view, 0, msi_export_row, &row_export_info );
msiobj_release( &view->hdr );
}
--
2.5.1

View File

@ -0,0 +1,110 @@
From 8c0efb4738cddee5545154a87fbdc40300bb5ddc Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Fri, 18 Sep 2015 12:19:19 -0600
Subject: msidb: Add support for wildcard (full database) export.
Signed-off-by: Erich E. Hoover <erich.e.hoover@wine-staging.com>
---
programs/msidb/main.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 78 insertions(+), 2 deletions(-)
diff --git a/programs/msidb/main.c b/programs/msidb/main.c
index 49dd11b..8796451 100644
--- a/programs/msidb/main.c
+++ b/programs/msidb/main.c
@@ -473,14 +473,90 @@ static int export_table( struct msidb_state *state, const WCHAR *table_name )
return 1;
}
+static int export_all_tables( struct msidb_state *state )
+{
+ static const WCHAR summary_information[] = {
+ '_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
+ static const char query_command[] = "SELECT Name FROM _Tables";
+ MSIHANDLE view = 0;
+ UINT ret;
+
+ ret = MsiDatabaseOpenViewA( state->database_handle, query_command, &view );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to open _Tables table.\n" );
+ goto cleanup;
+ }
+ ret = MsiViewExecute( view, 0 );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to query list from _Tables table.\n" );
+ goto cleanup;
+ }
+ for (;;)
+ {
+ MSIHANDLE record = 0;
+ WCHAR table[256];
+ DWORD size;
+
+ ret = MsiViewFetch( view, &record );
+ if (ret == ERROR_NO_MORE_ITEMS)
+ break;
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to query row from _Tables table.\n" );
+ goto cleanup;
+ }
+ size = sizeof(table)/sizeof(WCHAR);
+ ret = MsiRecordGetStringW( record, 1, table, &size );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to retrieve name string.\n" );
+ goto cleanup;
+ }
+ ret = MsiCloseHandle( record );
+ if (ret != ERROR_SUCCESS)
+ {
+ ERR( "Failed to close record handle.\n" );
+ goto cleanup;
+ }
+ if (!export_table( state, table ))
+ {
+ ret = ERROR_FUNCTION_FAILED;
+ goto cleanup;
+ }
+ }
+ ret = ERROR_SUCCESS;
+ /* the _SummaryInformation table is not listed in _Tables */
+ if (!export_table( state, summary_information ))
+ {
+ ret = ERROR_FUNCTION_FAILED;
+ goto cleanup;
+ }
+
+cleanup:
+ if (view)
+ MsiViewClose( view );
+ return (ret == ERROR_SUCCESS);
+}
+
static int export_tables( struct msidb_state *state )
{
+ const WCHAR wildcard[] = { '*',0 };
struct msidb_listentry *data;
LIST_FOR_EACH_ENTRY( data, &state->table_list, struct msidb_listentry, entry )
{
- if (!export_table( state, data->name ))
- return 0; /* failed, do not commit changes */
+ if (strcmpW( data->name, wildcard ) == 0)
+ {
+ if (!export_all_tables( state ))
+ return 0; /* failed, do not commit changes */
+ }
+ else
+ {
+ if (!export_table( state, data->name ))
+ return 0; /* failed, do not commit changes */
+ }
}
return 1;
}
--
2.5.1

View File

@ -0,0 +1 @@
Fixes: Add implementation for msidb commandline tool

View File

@ -170,6 +170,7 @@ patch_enable_all ()
enable_mountmgr_DosDevices="$1"
enable_mscoree_CorValidateImage="$1"
enable_mshtml_HTMLLocation_put_hash="$1"
enable_msidb_Implementation="$1"
enable_msvcp90_basic_string_dtor="$1"
enable_msvcrt_Math_Precision="$1"
enable_msvcrt_StdHandle_RefCount="$1"
@ -604,6 +605,9 @@ patch_enable ()
mshtml-HTMLLocation_put_hash)
enable_mshtml_HTMLLocation_put_hash="$2"
;;
msidb-Implementation)
enable_msidb_Implementation="$2"
;;
msvcp90-basic_string_dtor)
enable_msvcp90_basic_string_dtor="$2"
;;
@ -3691,6 +3695,43 @@ if test "$enable_mshtml_HTMLLocation_put_hash" -eq 1; then
) >> "$patchlist"
fi
# Patchset msidb-Implementation
# |
# | Modified files:
# | * configure.ac, dlls/msi/database.c, dlls/msi/msipriv.h, dlls/msi/streams.c, dlls/msi/suminfo.c,
# | programs/msidb/Makefile.in, programs/msidb/main.c
# |
if test "$enable_msidb_Implementation" -eq 1; then
patch_apply msidb-Implementation/0001-msidb-Add-stub-tool-for-manipulating-MSI-databases.patch
patch_apply msidb-Implementation/0002-msi-Return-an-error-when-MsiDatabaseImport-is-passed.patch
patch_apply msidb-Implementation/0003-msidb-Add-support-for-importing-database-tables.patch
patch_apply msidb-Implementation/0004-msidb-Add-support-for-adding-stream-cabinet-files-to.patch
patch_apply msidb-Implementation/0005-msi-Add-support-for-deleting-streams-from-an-MSI-dat.patch
patch_apply msidb-Implementation/0006-msidb-Add-support-for-removing-stream-cabinet-files-.patch
patch_apply msidb-Implementation/0007-msidb-Add-support-for-extracting-stream-cabinet-file.patch
patch_apply msidb-Implementation/0008-msidb-Add-support-for-exporting-database-tables.patch
patch_apply msidb-Implementation/0009-msidb-Add-support-for-exporting-with-short-DOS-filen.patch
patch_apply msidb-Implementation/0010-msi-Add-support-for-exporting-the-_SummaryInformatio.patch
patch_apply msidb-Implementation/0011-msi-Break-out-field-exporting-into-a-separate-routin.patch
patch_apply msidb-Implementation/0012-msi-Add-support-for-exporting-binary-streams-Binary-.patch
patch_apply msidb-Implementation/0013-msidb-Add-support-for-wildcard-full-database-export.patch
(
echo '+ { "Erich E. Hoover", "msidb: Add stub tool for manipulating MSI databases.", 1 },';
echo '+ { "Erich E. Hoover", "msi: Return an error when MsiDatabaseImport is passed an invalid pathname.", 1 },';
echo '+ { "Erich E. Hoover", "msidb: Add support for importing database tables.", 1 },';
echo '+ { "Erich E. Hoover", "msidb: Add support for adding stream/cabinet files to MSI databases.", 1 },';
echo '+ { "Erich E. Hoover", "msi: Add support for deleting streams from an MSI database.", 1 },';
echo '+ { "Erich E. Hoover", "msidb: Add support for removing stream/cabinet files from MSI databases.", 1 },';
echo '+ { "Erich E. Hoover", "msidb: Add support for extracting stream/cabinet files from MSI databases.", 1 },';
echo '+ { "Erich E. Hoover", "msidb: Add support for exporting database tables.", 1 },';
echo '+ { "Erich E. Hoover", "msidb: Add support for exporting with short (DOS) filenames.", 1 },';
echo '+ { "Erich E. Hoover", "msi: Add support for exporting the _SummaryInformation table.", 1 },';
echo '+ { "Erich E. Hoover", "msi: Break out field exporting into a separate routine.", 1 },';
echo '+ { "Erich E. Hoover", "msi: Add support for exporting binary streams (Binary/Icon tables).", 1 },';
echo '+ { "Erich E. Hoover", "msidb: Add support for wildcard (full database) export.", 1 },';
) >> "$patchlist"
fi
# Patchset msvcp90-basic_string_dtor
# |
# | This patchset fixes the following Wine bugs: