📄 table.c
字号:
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2002-2005 Mike McCormack for CodeWeavers
*
* 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
*/
#include <stdarg.h>
#define COBJMACROS
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "msi.h"
#include "msiquery.h"
#include "objbase.h"
#include "objidl.h"
#include "winnls.h"
#include "msipriv.h"
#include "query.h"
#include "assert.h"
#include "wine/debug.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(msidb);
#define MSITABLE_HASH_TABLE_SIZE 37
typedef struct tagMSICOLUMNHASHENTRY
{
struct tagMSICOLUMNHASHENTRY *next;
UINT value;
UINT row;
} MSICOLUMNHASHENTRY;
typedef struct tagMSICOLUMNINFO
{
LPCWSTR tablename;
UINT number;
LPCWSTR colname;
UINT type;
UINT offset;
MSICOLUMNHASHENTRY **hash_table;
} MSICOLUMNINFO;
struct tagMSITABLE
{
USHORT **data;
UINT row_count;
struct list entry;
WCHAR name[1];
};
typedef struct tagMSITRANSFORM {
struct list entry;
IStorage *stg;
} MSITRANSFORM;
static const WCHAR szStringData[] = {
'_','S','t','r','i','n','g','D','a','t','a',0 };
static const WCHAR szStringPool[] = {
'_','S','t','r','i','n','g','P','o','o','l',0 };
#define MAX_STREAM_NAME 0x1f
static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name,
MSICOLUMNINFO **pcols, UINT *pcount );
static UINT get_tablecolumns( MSIDATABASE *db,
LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz);
static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count );
static inline UINT bytes_per_column( const MSICOLUMNINFO *col )
{
if( col->type & MSITYPE_STRING )
return 2;
if( (col->type & 0xff) > 4 )
ERR("Invalid column size!\n");
return col->type & 0xff;
}
static int utf2mime(int x)
{
if( (x>='0') && (x<='9') )
return x-'0';
if( (x>='A') && (x<='Z') )
return x-'A'+10;
if( (x>='a') && (x<='z') )
return x-'a'+10+26;
if( x=='.' )
return 10+26+26;
if( x=='_' )
return 10+26+26+1;
return -1;
}
static LPWSTR encode_streamname(BOOL bTable, LPCWSTR in)
{
DWORD count = MAX_STREAM_NAME;
DWORD ch, next;
LPWSTR out, p;
if( !bTable )
count = lstrlenW( in )+2;
out = msi_alloc( count*sizeof(WCHAR) );
p = out;
if( bTable )
{
*p++ = 0x4840;
count --;
}
while( count -- )
{
ch = *in++;
if( !ch )
{
*p = ch;
return out;
}
if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
{
ch = utf2mime(ch) + 0x4800;
next = *in;
if( next && (next<0x80) )
{
next = utf2mime(next);
if( next >= 0 )
{
next += 0x3ffffc0;
ch += (next<<6);
in++;
}
}
}
*p++ = ch;
}
ERR("Failed to encode stream name (%s)\n",debugstr_w(in));
msi_free( out );
return NULL;
}
static int mime2utf(int x)
{
if( x<10 )
return x + '0';
if( x<(10+26))
return x - 10 + 'A';
if( x<(10+26+26))
return x - 10 - 26 + 'a';
if( x == (10+26+26) )
return '.';
return '_';
}
static BOOL decode_streamname(LPWSTR in, LPWSTR out)
{
WCHAR ch;
DWORD count = 0;
while ( (ch = *in++) )
{
if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
{
if( ch >= 0x4800 )
ch = mime2utf(ch-0x4800);
else
{
ch -= 0x3800;
*out++ = mime2utf(ch&0x3f);
count++;
ch = mime2utf((ch>>6)&0x3f);
}
}
*out++ = ch;
count++;
}
*out = 0;
return count;
}
void enum_stream_names( IStorage *stg )
{
IEnumSTATSTG *stgenum = NULL;
HRESULT r;
STATSTG stat;
ULONG n, count;
WCHAR name[0x40];
r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
if( FAILED( r ) )
return;
n = 0;
while( 1 )
{
count = 0;
r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
if( FAILED( r ) || !count )
break;
decode_streamname( stat.pwcsName, name );
TRACE("stream %2d -> %s %s\n", n,
debugstr_w(stat.pwcsName), debugstr_w(name) );
n++;
}
IEnumSTATSTG_Release( stgenum );
}
static UINT read_stream_data( IStorage *stg, LPCWSTR stname,
USHORT **pdata, UINT *psz )
{
HRESULT r;
UINT ret = ERROR_FUNCTION_FAILED;
VOID *data;
ULONG sz, count;
IStream *stm = NULL;
STATSTG stat;
LPWSTR encname;
encname = encode_streamname(TRUE, stname);
TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
r = IStorage_OpenStream(stg, encname, NULL,
STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
msi_free( encname );
if( FAILED( r ) )
{
WARN("open stream failed r = %08x - empty table?\n", r);
return ret;
}
r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
if( FAILED( r ) )
{
WARN("open stream failed r = %08x!\n", r);
goto end;
}
if( stat.cbSize.QuadPart >> 32 )
{
WARN("Too big!\n");
goto end;
}
sz = stat.cbSize.QuadPart;
data = msi_alloc( sz );
if( !data )
{
WARN("couldn't allocate memory r=%08x!\n", r);
ret = ERROR_NOT_ENOUGH_MEMORY;
goto end;
}
r = IStream_Read(stm, data, sz, &count );
if( FAILED( r ) || ( count != sz ) )
{
msi_free( data );
WARN("read stream failed r = %08x!\n", r);
goto end;
}
*pdata = data;
*psz = sz;
ret = ERROR_SUCCESS;
end:
IStream_Release( stm );
return ret;
}
UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
{
LPWSTR encname;
HRESULT r;
encname = encode_streamname(FALSE, stname);
TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
r = IStorage_OpenStream(db->storage, encname, NULL,
STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm);
if( FAILED( r ) )
{
MSITRANSFORM *transform;
LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry )
{
TRACE("looking for %s in transform storage\n", debugstr_w(stname) );
r = IStorage_OpenStream( transform->stg, encname, NULL,
STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
if (SUCCEEDED(r))
break;
}
}
msi_free( encname );
return SUCCEEDED(r) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED;
}
UINT read_raw_stream_data( MSIDATABASE *db, LPCWSTR stname,
USHORT **pdata, UINT *psz )
{
HRESULT r;
UINT ret = ERROR_FUNCTION_FAILED;
VOID *data;
ULONG sz, count;
IStream *stm = NULL;
STATSTG stat;
r = db_get_raw_stream( db, stname, &stm );
if( r != ERROR_SUCCESS)
return ret;
r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
if( FAILED( r ) )
{
WARN("open stream failed r = %08x!\n", r);
goto end;
}
if( stat.cbSize.QuadPart >> 32 )
{
WARN("Too big!\n");
goto end;
}
sz = stat.cbSize.QuadPart;
data = msi_alloc( sz );
if( !data )
{
WARN("couldn't allocate memory r=%08x!\n", r);
ret = ERROR_NOT_ENOUGH_MEMORY;
goto end;
}
r = IStream_Read(stm, data, sz, &count );
if( FAILED( r ) || ( count != sz ) )
{
msi_free( data );
WARN("read stream failed r = %08x!\n", r);
goto end;
}
*pdata = data;
*psz = sz;
ret = ERROR_SUCCESS;
end:
IStream_Release( stm );
return ret;
}
static UINT write_stream_data( IStorage *stg, LPCWSTR stname,
LPVOID data, UINT sz )
{
HRESULT r;
UINT ret = ERROR_FUNCTION_FAILED;
ULONG count;
IStream *stm = NULL;
ULARGE_INTEGER size;
LARGE_INTEGER pos;
LPWSTR encname;
encname = encode_streamname(TRUE, stname );
r = IStorage_OpenStream( stg, encname, NULL,
STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
if( FAILED(r) )
{
r = IStorage_CreateStream( stg, encname,
STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
}
msi_free( encname );
if( FAILED( r ) )
{
WARN("open stream failed r = %08x\n", r);
return ret;
}
size.QuadPart = sz;
r = IStream_SetSize( stm, size );
if( FAILED( r ) )
{
WARN("Failed to SetSize\n");
goto end;
}
pos.QuadPart = 0;
r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
if( FAILED( r ) )
{
WARN("Failed to Seek\n");
goto end;
}
r = IStream_Write(stm, data, sz, &count );
if( FAILED( r ) || ( count != sz ) )
{
WARN("Failed to Write\n");
goto end;
}
ret = ERROR_SUCCESS;
end:
IStream_Release( stm );
return ret;
}
static void free_table( MSITABLE *table )
{
int i;
for( i=0; i<table->row_count; i++ )
msi_free( table->data[i] );
msi_free( table->data );
msi_free( table );
}
static UINT msi_table_get_row_size( const MSICOLUMNINFO *cols, UINT count )
{
const MSICOLUMNINFO *last_col = &cols[count-1];
if (!count)
return 0;
return last_col->offset + bytes_per_column( last_col );
}
/* add this table to the list of cached tables in the database */
static MSITABLE *read_table_from_storage( IStorage *stg, LPCWSTR name,
const MSICOLUMNINFO *cols, UINT num_cols )
{
MSITABLE *t;
USHORT *rawdata = NULL;
UINT rawsize = 0, i, j, row_size = 0;
TRACE("%s\n",debugstr_w(name));
/* nonexistent tables should be interpreted as empty tables */
t = msi_alloc( sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
if( !t )
return t;
row_size = msi_table_get_row_size( cols, num_cols );
t->row_count = 0;
t->data = NULL;
lstrcpyW( t->name, name );
/* if we can't read the table, just assume that it's empty */
read_stream_data( stg, name, &rawdata, &rawsize );
if( !rawdata )
return t;
TRACE("Read %d bytes\n", rawsize );
if( rawsize % row_size )
{
WARN("Table size is invalid %d/%d\n", rawsize, row_size );
goto err;
}
t->row_count = rawsize / row_size;
t->data = msi_alloc_zero( t->row_count * sizeof (USHORT*) );
if( !t->data )
goto err;
/* transpose all the data */
TRACE("Transposing data from %d rows\n", t->row_count );
for( i=0; i<t->row_count; i++ )
{
t->data[i] = msi_alloc( row_size );
if( !t->data[i] )
goto err;
for( j=0; j<num_cols; j++ )
{
UINT ofs = cols[j].offset/2;
UINT n = bytes_per_column( &cols[j] );
switch( n )
{
case 2:
t->data[i][ofs] = rawdata[ofs*t->row_count + i ];
break;
case 4:
t->data[i][ofs] = rawdata[ofs*t->row_count + i*2 ];
t->data[i][ofs+1] = rawdata[ofs*t->row_count + i*2 + 1];
break;
default:
ERR("oops - unknown column width %d\n", n);
goto err;
}
}
}
msi_free( rawdata );
return t;
err:
msi_free( rawdata );
free_table( t );
return NULL;
}
void free_cached_tables( MSIDATABASE *db )
{
while( !list_empty( &db->tables ) )
{
MSITABLE *t = LIST_ENTRY( list_head( &db->tables ), MSITABLE, entry );
list_remove( &t->entry );
free_table( t );
}
}
static MSITABLE *find_cached_table( MSIDATABASE *db, LPCWSTR name )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -