mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2025-01-28 22:04:43 -08:00
Added patches for msidb commandline utility (to read and write *.msi files).
This commit is contained in:
parent
4ad4553546
commit
456eb2f553
@ -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
1
debian/changelog
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
1
patches/msidb-Implementation/definition
Normal file
1
patches/msidb-Implementation/definition
Normal file
@ -0,0 +1 @@
|
||||
Fixes: Add implementation for msidb commandline tool
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user