msi: Rewrite the folder resolution code to keep track of the parent-child relationship.
This commit is contained in:
parent
8fb5368d4e
commit
4668091528
|
@ -2056,16 +2056,14 @@ static UINT load_folder( MSIRECORD *row, LPVOID param )
|
||||||
LPWSTR p, tgt_short, tgt_long, src_short, src_long;
|
LPWSTR p, tgt_short, tgt_long, src_short, src_long;
|
||||||
MSIFOLDER *folder;
|
MSIFOLDER *folder;
|
||||||
|
|
||||||
folder = msi_alloc_zero( sizeof (MSIFOLDER) );
|
if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
|
||||||
if (!folder)
|
list_init( &folder->children );
|
||||||
return ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
|
|
||||||
folder->Directory = msi_dup_record_field( row, 1 );
|
folder->Directory = msi_dup_record_field( row, 1 );
|
||||||
|
folder->Parent = msi_dup_record_field( row, 2 );
|
||||||
|
p = msi_dup_record_field(row, 3);
|
||||||
|
|
||||||
TRACE("%s\n", debugstr_w(folder->Directory));
|
TRACE("%s\n", debugstr_w(folder->Directory));
|
||||||
|
|
||||||
p = msi_dup_record_field(row, 3);
|
|
||||||
|
|
||||||
/* split src and target dir */
|
/* split src and target dir */
|
||||||
tgt_short = p;
|
tgt_short = p;
|
||||||
src_short = folder_split_path( p, ':' );
|
src_short = folder_split_path( p, ':' );
|
||||||
|
@ -2101,17 +2099,36 @@ static UINT load_folder( MSIRECORD *row, LPVOID param )
|
||||||
TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
|
TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
|
||||||
TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
|
TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
|
||||||
|
|
||||||
folder->Parent = msi_dup_record_field( row, 2 );
|
|
||||||
|
|
||||||
folder->Property = msi_dup_property( package->db, folder->Directory );
|
|
||||||
|
|
||||||
list_add_tail( &package->folders, &folder->entry );
|
list_add_tail( &package->folders, &folder->entry );
|
||||||
|
|
||||||
TRACE("returning %p\n", folder);
|
|
||||||
|
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
|
||||||
|
{
|
||||||
|
FolderList *fl;
|
||||||
|
|
||||||
|
if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
|
||||||
|
fl->folder = child;
|
||||||
|
list_add_tail( &parent->children, &fl->entry );
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UINT find_folder_children( MSIRECORD *row, LPVOID param )
|
||||||
|
{
|
||||||
|
MSIPACKAGE *package = param;
|
||||||
|
MSIFOLDER *parent, *child;
|
||||||
|
|
||||||
|
if (!(child = get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
|
||||||
|
return ERROR_FUNCTION_FAILED;
|
||||||
|
|
||||||
|
if (!child->Parent) return ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if (!(parent = get_loaded_folder( package, child->Parent )))
|
||||||
|
return ERROR_FUNCTION_FAILED;
|
||||||
|
|
||||||
|
return add_folder_child( parent, child );
|
||||||
|
}
|
||||||
|
|
||||||
static UINT load_all_folders( MSIPACKAGE *package )
|
static UINT load_all_folders( MSIPACKAGE *package )
|
||||||
{
|
{
|
||||||
static const WCHAR query[] = {
|
static const WCHAR query[] = {
|
||||||
|
@ -2127,24 +2144,17 @@ static UINT load_all_folders( MSIPACKAGE *package )
|
||||||
if (r != ERROR_SUCCESS)
|
if (r != ERROR_SUCCESS)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = MSI_IterateRecords(view, NULL, load_folder, package);
|
r = MSI_IterateRecords( view, NULL, load_folder, package );
|
||||||
msiobj_release(&view->hdr);
|
if (r != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
msiobj_release( &view->hdr );
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
r = MSI_IterateRecords( view, NULL, find_folder_children, package );
|
||||||
|
msiobj_release( &view->hdr );
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* I am not doing any of the costing functionality yet.
|
|
||||||
* Mostly looking at doing the Component and Feature loading
|
|
||||||
*
|
|
||||||
* The native MSI does A LOT of modification to tables here. Mostly adding
|
|
||||||
* a lot of temporary columns to the Feature and Component tables.
|
|
||||||
*
|
|
||||||
* note: Native msi also tracks the short filename. But I am only going to
|
|
||||||
* track the long ones. Also looking at this directory table
|
|
||||||
* it appears that the directory table does not get the parents
|
|
||||||
* resolved base on property only based on their entries in the
|
|
||||||
* directory table.
|
|
||||||
*/
|
|
||||||
static UINT ACTION_CostInitialize(MSIPACKAGE *package)
|
static UINT ACTION_CostInitialize(MSIPACKAGE *package)
|
||||||
{
|
{
|
||||||
msi_set_property( package->db, szCostingComplete, szZero );
|
msi_set_property( package->db, szCostingComplete, szZero );
|
||||||
|
@ -2554,30 +2564,6 @@ UINT MSI_SetFeatureStates(MSIPACKAGE *package)
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
|
|
||||||
{
|
|
||||||
MSIPACKAGE *package = param;
|
|
||||||
LPCWSTR name;
|
|
||||||
LPWSTR path;
|
|
||||||
MSIFOLDER *f;
|
|
||||||
|
|
||||||
name = MSI_RecordGetString(row,1);
|
|
||||||
|
|
||||||
f = get_loaded_folder(package, name);
|
|
||||||
if (!f) return ERROR_SUCCESS;
|
|
||||||
|
|
||||||
/* reset the ResolvedTarget */
|
|
||||||
msi_free(f->ResolvedTarget);
|
|
||||||
f->ResolvedTarget = NULL;
|
|
||||||
|
|
||||||
TRACE("directory %s ...\n", debugstr_w(name));
|
|
||||||
path = resolve_target_folder( package, name, TRUE, TRUE, NULL );
|
|
||||||
TRACE("resolves to %s\n", debugstr_w(path));
|
|
||||||
msi_free(path);
|
|
||||||
|
|
||||||
return ERROR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
|
static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
|
||||||
{
|
{
|
||||||
MSIPACKAGE *package = param;
|
MSIPACKAGE *package = param;
|
||||||
|
@ -2782,20 +2768,78 @@ static UINT calculate_file_cost( MSIPACKAGE *package )
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
void msi_clean_path( WCHAR *p )
|
||||||
* A lot is done in this function aside from just the costing.
|
{
|
||||||
* The costing needs to be implemented at some point but for now I am going
|
WCHAR *q = p;
|
||||||
* to focus on the directory building
|
int n, len = 0;
|
||||||
*
|
|
||||||
*/
|
while (1)
|
||||||
|
{
|
||||||
|
/* copy until the end of the string or a space */
|
||||||
|
while (*p != ' ' && (*q = *p))
|
||||||
|
{
|
||||||
|
p++, len++;
|
||||||
|
/* reduce many backslashes to one */
|
||||||
|
if (*p != '\\' || *q != '\\')
|
||||||
|
q++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* quit at the end of the string */
|
||||||
|
if (!*p)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* count the number of spaces */
|
||||||
|
n = 0;
|
||||||
|
while (p[n] == ' ')
|
||||||
|
n++;
|
||||||
|
|
||||||
|
/* if it's leading or trailing space, skip it */
|
||||||
|
if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
|
||||||
|
p += n;
|
||||||
|
else /* copy n spaces */
|
||||||
|
while (n && (*q++ = *p++)) n--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
|
||||||
|
{
|
||||||
|
FolderList *fl;
|
||||||
|
MSIFOLDER *folder, *parent, *child;
|
||||||
|
WCHAR *path;
|
||||||
|
|
||||||
|
TRACE("resolving %s\n", debugstr_w(name));
|
||||||
|
|
||||||
|
if (!(folder = get_loaded_folder( package, name ))) return;
|
||||||
|
|
||||||
|
if (!strcmpW( folder->Directory, cszTargetDir )) /* special resolving for target root dir */
|
||||||
|
{
|
||||||
|
if (!load_prop || !(path = msi_dup_property( package->db, cszTargetDir )))
|
||||||
|
{
|
||||||
|
path = msi_dup_property( package->db, cszRootDrive );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
|
||||||
|
{
|
||||||
|
parent = get_loaded_folder( package, folder->Parent );
|
||||||
|
path = build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
|
||||||
|
}
|
||||||
|
msi_clean_path( path );
|
||||||
|
msi_set_property( package->db, folder->Directory, path );
|
||||||
|
msi_free( folder->ResolvedTarget );
|
||||||
|
folder->ResolvedTarget = path;
|
||||||
|
|
||||||
|
LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
|
||||||
|
{
|
||||||
|
child = fl->folder;
|
||||||
|
msi_resolve_target_folder( package, child->Directory, load_prop );
|
||||||
|
}
|
||||||
|
TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
|
||||||
|
}
|
||||||
|
|
||||||
static UINT ACTION_CostFinalize(MSIPACKAGE *package)
|
static UINT ACTION_CostFinalize(MSIPACKAGE *package)
|
||||||
{
|
{
|
||||||
static const WCHAR ExecSeqQuery[] =
|
static const WCHAR condition_query[] =
|
||||||
{'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
|
{'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','C','o','n','d','i','t','i','o','n','`',0};
|
||||||
'`','D','i','r','e','c','t','o','r','y','`',0};
|
|
||||||
static const WCHAR ConditionQuery[] =
|
|
||||||
{'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
|
|
||||||
'`','C','o','n','d','i','t','i','o','n','`',0};
|
|
||||||
static const WCHAR szlevel[] =
|
static const WCHAR szlevel[] =
|
||||||
{'I','N','S','T','A','L','L','L','E','V','E','L',0};
|
{'I','N','S','T','A','L','L','L','E','V','E','L',0};
|
||||||
static const WCHAR szOutOfDiskSpace[] =
|
static const WCHAR szOutOfDiskSpace[] =
|
||||||
|
@ -2805,15 +2849,8 @@ static UINT ACTION_CostFinalize(MSIPACKAGE *package)
|
||||||
MSIQUERY * view;
|
MSIQUERY * view;
|
||||||
LPWSTR level;
|
LPWSTR level;
|
||||||
|
|
||||||
TRACE("Building Directory properties\n");
|
TRACE("Building directory properties\n");
|
||||||
|
msi_resolve_target_folder( package, cszTargetDir, TRUE );
|
||||||
rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
|
|
||||||
if (rc == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
|
|
||||||
package);
|
|
||||||
msiobj_release(&view->hdr);
|
|
||||||
}
|
|
||||||
|
|
||||||
TRACE("Evaluating component conditions\n");
|
TRACE("Evaluating component conditions\n");
|
||||||
LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
|
LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
|
||||||
|
@ -2835,7 +2872,7 @@ static UINT ACTION_CostFinalize(MSIPACKAGE *package)
|
||||||
{
|
{
|
||||||
TRACE("Evaluating feature conditions\n");
|
TRACE("Evaluating feature conditions\n");
|
||||||
|
|
||||||
rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
|
rc = MSI_DatabaseOpenViewW( package->db, condition_query, &view );
|
||||||
if (rc == ERROR_SUCCESS)
|
if (rc == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
|
rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
|
||||||
|
|
|
@ -34,7 +34,6 @@
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(msi);
|
WINE_DEFAULT_DEBUG_CHANNEL(msi);
|
||||||
|
|
||||||
static const WCHAR cszTargetDir[] = {'T','A','R','G','E','T','D','I','R',0};
|
|
||||||
static const WCHAR cszDatabase[]={'D','A','T','A','B','A','S','E',0};
|
static const WCHAR cszDatabase[]={'D','A','T','A','B','A','S','E',0};
|
||||||
|
|
||||||
LPWSTR build_icon_path(MSIPACKAGE *package, LPCWSTR icon_name )
|
LPWSTR build_icon_path(MSIPACKAGE *package, LPCWSTR icon_name )
|
||||||
|
@ -198,45 +197,6 @@ static LPWSTR get_source_root( MSIPACKAGE *package )
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* clean_spaces_from_path()
|
|
||||||
*
|
|
||||||
* removes spaces from the beginning and end of path segments
|
|
||||||
* removes multiple \\ characters
|
|
||||||
*/
|
|
||||||
static void clean_spaces_from_path( LPWSTR p )
|
|
||||||
{
|
|
||||||
LPWSTR q = p;
|
|
||||||
int n, len = 0;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
/* copy until the end of the string or a space */
|
|
||||||
while (*p != ' ' && (*q = *p))
|
|
||||||
{
|
|
||||||
p++, len++;
|
|
||||||
/* reduce many backslashes to one */
|
|
||||||
if (*p != '\\' || *q != '\\')
|
|
||||||
q++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* quit at the end of the string */
|
|
||||||
if (!*p)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* count the number of spaces */
|
|
||||||
n = 0;
|
|
||||||
while (p[n] == ' ')
|
|
||||||
n++;
|
|
||||||
|
|
||||||
/* if it's leading or trailing space, skip it */
|
|
||||||
if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
|
|
||||||
p += n;
|
|
||||||
else /* copy n spaces */
|
|
||||||
while (n && (*q++ = *p++)) n--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LPWSTR resolve_file_source(MSIPACKAGE *package, MSIFILE *file)
|
LPWSTR resolve_file_source(MSIPACKAGE *package, MSIFILE *file)
|
||||||
{
|
{
|
||||||
LPWSTR p, path;
|
LPWSTR p, path;
|
||||||
|
@ -315,96 +275,6 @@ LPWSTR resolve_source_folder( MSIPACKAGE *package, LPCWSTR name, MSIFOLDER **fol
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
const WCHAR *msi_get_target_folder( MSIPACKAGE *package, const WCHAR *name )
|
|
||||||
{
|
|
||||||
MSIFOLDER *folder = get_loaded_folder( package, name );
|
|
||||||
if (folder) return folder->ResolvedTarget;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
LPWSTR resolve_target_folder( MSIPACKAGE *package, LPCWSTR name, BOOL set_prop, BOOL load_prop,
|
|
||||||
MSIFOLDER **folder )
|
|
||||||
{
|
|
||||||
MSIFOLDER *f;
|
|
||||||
LPWSTR p, path = NULL, parent;
|
|
||||||
|
|
||||||
TRACE("working to resolve %s\n", debugstr_w(name));
|
|
||||||
|
|
||||||
f = get_loaded_folder( package, name );
|
|
||||||
if (!f)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* special resolving for Target and Source root dir */
|
|
||||||
if (!strcmpW( name, cszTargetDir ))
|
|
||||||
{
|
|
||||||
if (!f->ResolvedTarget && !f->Property)
|
|
||||||
{
|
|
||||||
LPWSTR check_path;
|
|
||||||
check_path = msi_dup_property( package->db, cszTargetDir );
|
|
||||||
if (!check_path)
|
|
||||||
{
|
|
||||||
check_path = msi_dup_property( package->db, cszRootDrive );
|
|
||||||
if (set_prop)
|
|
||||||
msi_set_property( package->db, cszTargetDir, check_path );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* correct misbuilt target dir */
|
|
||||||
path = build_directory_name(2, check_path, NULL);
|
|
||||||
clean_spaces_from_path( path );
|
|
||||||
if (strcmpiW( path, check_path ))
|
|
||||||
msi_set_property( package->db, cszTargetDir, path );
|
|
||||||
msi_free(check_path);
|
|
||||||
|
|
||||||
f->ResolvedTarget = path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (folder)
|
|
||||||
*folder = f;
|
|
||||||
|
|
||||||
if (f->ResolvedTarget)
|
|
||||||
{
|
|
||||||
path = strdupW( f->ResolvedTarget );
|
|
||||||
TRACE(" already resolved to %s\n", debugstr_w(path));
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (f->Property)
|
|
||||||
{
|
|
||||||
path = build_directory_name( 2, f->Property, NULL );
|
|
||||||
TRACE(" internally set to %s\n", debugstr_w(path));
|
|
||||||
if (set_prop) msi_set_property( package->db, name, path );
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (load_prop && (path = msi_dup_property( package->db, name )))
|
|
||||||
{
|
|
||||||
f->ResolvedTarget = strdupW( path );
|
|
||||||
TRACE(" property set to %s\n", debugstr_w(path));
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!f->Parent)
|
|
||||||
return path;
|
|
||||||
|
|
||||||
parent = f->Parent;
|
|
||||||
|
|
||||||
TRACE(" ! parent is %s\n", debugstr_w(parent));
|
|
||||||
|
|
||||||
p = resolve_target_folder( package, parent, set_prop, load_prop, NULL );
|
|
||||||
|
|
||||||
TRACE(" TargetDefault = %s\n", debugstr_w(f->TargetDefault));
|
|
||||||
path = build_directory_name( 3, p, f->TargetDefault, NULL );
|
|
||||||
clean_spaces_from_path( path );
|
|
||||||
f->ResolvedTarget = strdupW( path );
|
|
||||||
|
|
||||||
TRACE("-> %s\n", debugstr_w(path));
|
|
||||||
if (set_prop) msi_set_property( package->db, name, path );
|
|
||||||
msi_free( p );
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* wrapper to resist a need for a full rewrite right now */
|
/* wrapper to resist a need for a full rewrite right now */
|
||||||
DWORD deformat_string(MSIPACKAGE *package, LPCWSTR ptr, WCHAR** data )
|
DWORD deformat_string(MSIPACKAGE *package, LPCWSTR ptr, WCHAR** data )
|
||||||
{
|
{
|
||||||
|
|
|
@ -212,6 +212,13 @@ UINT msi_strcpy_to_awstring( LPCWSTR str, awstring *awbuf, DWORD *sz )
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const WCHAR *msi_get_target_folder( MSIPACKAGE *package, const WCHAR *name )
|
||||||
|
{
|
||||||
|
MSIFOLDER *folder = get_loaded_folder( package, name );
|
||||||
|
if (folder) return folder->ResolvedTarget;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
* MsiGetTargetPath (internal)
|
* MsiGetTargetPath (internal)
|
||||||
*/
|
*/
|
||||||
|
@ -219,7 +226,7 @@ static UINT MSI_GetTargetPath( MSIHANDLE hInstall, LPCWSTR szFolder,
|
||||||
awstring *szPathBuf, LPDWORD pcchPathBuf )
|
awstring *szPathBuf, LPDWORD pcchPathBuf )
|
||||||
{
|
{
|
||||||
MSIPACKAGE *package;
|
MSIPACKAGE *package;
|
||||||
LPWSTR path;
|
const WCHAR *path;
|
||||||
UINT r = ERROR_FUNCTION_FAILED;
|
UINT r = ERROR_FUNCTION_FAILED;
|
||||||
|
|
||||||
if (!szFolder)
|
if (!szFolder)
|
||||||
|
@ -280,14 +287,13 @@ done:
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
path = resolve_target_folder( package, szFolder, FALSE, TRUE, NULL );
|
path = msi_get_target_folder( package, szFolder );
|
||||||
msiobj_release( &package->hdr );
|
msiobj_release( &package->hdr );
|
||||||
|
|
||||||
if (!path)
|
if (!path)
|
||||||
return ERROR_DIRECTORY;
|
return ERROR_DIRECTORY;
|
||||||
|
|
||||||
r = msi_strcpy_to_awstring( path, szPathBuf, pcchPathBuf );
|
r = msi_strcpy_to_awstring( path, szPathBuf, pcchPathBuf );
|
||||||
msi_free( path );
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,80 +491,63 @@ end:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void set_target_path( MSIPACKAGE *package, MSIFOLDER *folder, const WCHAR *path )
|
||||||
* Ok my original interpretation of this was wrong. And it looks like msdn has
|
|
||||||
* changed a bit also. The given folder path does not have to actually already
|
|
||||||
* exist, it just cannot be read only and must be a legal folder path.
|
|
||||||
*/
|
|
||||||
UINT MSI_SetTargetPathW(MSIPACKAGE *package, LPCWSTR szFolder,
|
|
||||||
LPCWSTR szFolderPath)
|
|
||||||
{
|
{
|
||||||
DWORD attrib;
|
FolderList *fl;
|
||||||
LPWSTR path = NULL;
|
MSIFOLDER *child;
|
||||||
LPWSTR path2 = NULL;
|
|
||||||
|
msi_free( folder->ResolvedTarget );
|
||||||
|
folder->ResolvedTarget = strdupW( path );
|
||||||
|
msi_clean_path( folder->ResolvedTarget );
|
||||||
|
msi_set_property( package->db, folder->Directory, folder->ResolvedTarget );
|
||||||
|
|
||||||
|
LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
|
||||||
|
{
|
||||||
|
child = fl->folder;
|
||||||
|
msi_resolve_target_folder( package, child->Directory, FALSE );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT MSI_SetTargetPathW( MSIPACKAGE *package, LPCWSTR szFolder, LPCWSTR szFolderPath )
|
||||||
|
{
|
||||||
|
DWORD attrib, len;
|
||||||
MSIFOLDER *folder;
|
MSIFOLDER *folder;
|
||||||
MSIFILE *file;
|
MSIFILE *file;
|
||||||
|
|
||||||
TRACE("%p %s %s\n",package, debugstr_w(szFolder),debugstr_w(szFolderPath));
|
TRACE("%p %s %s\n", package, debugstr_w(szFolder), debugstr_w(szFolderPath));
|
||||||
|
|
||||||
attrib = GetFileAttributesW(szFolderPath);
|
attrib = GetFileAttributesW(szFolderPath);
|
||||||
/* native MSI tests writeability by making temporary files at each drive */
|
/* native MSI tests writeability by making temporary files at each drive */
|
||||||
if ( attrib != INVALID_FILE_ATTRIBUTES &&
|
if (attrib != INVALID_FILE_ATTRIBUTES &&
|
||||||
(attrib & FILE_ATTRIBUTE_OFFLINE ||
|
(attrib & FILE_ATTRIBUTE_OFFLINE || attrib & FILE_ATTRIBUTE_READONLY))
|
||||||
attrib & FILE_ATTRIBUTE_READONLY))
|
{
|
||||||
return ERROR_FUNCTION_FAILED;
|
return ERROR_FUNCTION_FAILED;
|
||||||
|
|
||||||
path = resolve_target_folder( package, szFolder, FALSE, FALSE, &folder );
|
|
||||||
if (!path)
|
|
||||||
return ERROR_DIRECTORY;
|
|
||||||
|
|
||||||
msi_free(folder->Property);
|
|
||||||
folder->Property = build_directory_name(2, szFolderPath, NULL);
|
|
||||||
|
|
||||||
if (!strcmpiW( path, folder->Property ))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Resolved Target has not really changed, so just
|
|
||||||
* set this folder and do not recalculate everything.
|
|
||||||
*/
|
|
||||||
msi_free(folder->ResolvedTarget);
|
|
||||||
folder->ResolvedTarget = NULL;
|
|
||||||
path2 = resolve_target_folder( package, szFolder, TRUE, FALSE, NULL );
|
|
||||||
msi_free(path2);
|
|
||||||
}
|
}
|
||||||
else
|
if (!(folder = get_loaded_folder( package, szFolder ))) return ERROR_DIRECTORY;
|
||||||
|
|
||||||
|
len = strlenW( szFolderPath );
|
||||||
|
if (len && szFolderPath[len - 1] != '\\')
|
||||||
{
|
{
|
||||||
MSIFOLDER *f;
|
WCHAR *path = msi_alloc( (len + 2) * sizeof(WCHAR) );
|
||||||
|
memcpy( path, szFolderPath, len * sizeof(WCHAR) );
|
||||||
LIST_FOR_EACH_ENTRY( f, &package->folders, MSIFOLDER, entry )
|
path[len] = '\\';
|
||||||
{
|
path[len + 1] = 0;
|
||||||
msi_free(f->ResolvedTarget);
|
set_target_path( package, folder, path );
|
||||||
f->ResolvedTarget=NULL;
|
msi_free( path );
|
||||||
}
|
|
||||||
|
|
||||||
LIST_FOR_EACH_ENTRY( f, &package->folders, MSIFOLDER, entry )
|
|
||||||
{
|
|
||||||
path2 = resolve_target_folder( package, f->Directory, TRUE, FALSE, NULL );
|
|
||||||
msi_free(path2);
|
|
||||||
}
|
|
||||||
|
|
||||||
LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
|
|
||||||
{
|
|
||||||
MSICOMPONENT *comp = file->Component;
|
|
||||||
LPWSTR dir;
|
|
||||||
|
|
||||||
if (!comp->Enabled || (comp->assembly && !comp->assembly->application))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
dir = resolve_target_folder( package, comp->Directory, FALSE, FALSE, NULL );
|
|
||||||
msi_free(file->TargetPath);
|
|
||||||
|
|
||||||
file->TargetPath = build_directory_name(2, dir, file->FileName);
|
|
||||||
msi_free(dir);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
msi_free(path);
|
else set_target_path( package, folder, szFolderPath );
|
||||||
|
|
||||||
|
LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
|
||||||
|
{
|
||||||
|
const WCHAR *dir;
|
||||||
|
MSICOMPONENT *comp = file->Component;
|
||||||
|
|
||||||
|
if (!comp->Enabled || (comp->assembly && !comp->assembly->application)) continue;
|
||||||
|
|
||||||
|
dir = msi_get_target_folder( package, comp->Directory );
|
||||||
|
msi_free( file->TargetPath );
|
||||||
|
file->TargetPath = build_directory_name( 2, dir, file->FileName );
|
||||||
|
}
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -489,6 +489,7 @@ typedef struct tagFeatureList
|
||||||
typedef struct tagMSIFOLDER
|
typedef struct tagMSIFOLDER
|
||||||
{
|
{
|
||||||
struct list entry;
|
struct list entry;
|
||||||
|
struct list children;
|
||||||
LPWSTR Directory;
|
LPWSTR Directory;
|
||||||
LPWSTR Parent;
|
LPWSTR Parent;
|
||||||
LPWSTR TargetDefault;
|
LPWSTR TargetDefault;
|
||||||
|
@ -497,7 +498,6 @@ typedef struct tagMSIFOLDER
|
||||||
|
|
||||||
LPWSTR ResolvedTarget;
|
LPWSTR ResolvedTarget;
|
||||||
LPWSTR ResolvedSource;
|
LPWSTR ResolvedSource;
|
||||||
LPWSTR Property; /* initially set property */
|
|
||||||
INT State;
|
INT State;
|
||||||
/* 0 = uninitialized */
|
/* 0 = uninitialized */
|
||||||
/* 1 = existing */
|
/* 1 = existing */
|
||||||
|
@ -507,6 +507,12 @@ typedef struct tagMSIFOLDER
|
||||||
INT Space;
|
INT Space;
|
||||||
} MSIFOLDER;
|
} MSIFOLDER;
|
||||||
|
|
||||||
|
typedef struct tagFolderList
|
||||||
|
{
|
||||||
|
struct list entry;
|
||||||
|
MSIFOLDER *folder;
|
||||||
|
} FolderList;
|
||||||
|
|
||||||
typedef enum _msi_file_state {
|
typedef enum _msi_file_state {
|
||||||
msifs_invalid,
|
msifs_invalid,
|
||||||
msifs_missing,
|
msifs_missing,
|
||||||
|
@ -969,7 +975,8 @@ extern UINT msi_set_property( MSIDATABASE *, LPCWSTR, LPCWSTR ) DECLSPEC_HIDDEN;
|
||||||
extern UINT msi_get_property( MSIDATABASE *, LPCWSTR, LPWSTR, LPDWORD ) DECLSPEC_HIDDEN;
|
extern UINT msi_get_property( MSIDATABASE *, LPCWSTR, LPWSTR, LPDWORD ) DECLSPEC_HIDDEN;
|
||||||
extern int msi_get_property_int( MSIDATABASE *package, LPCWSTR prop, int def ) DECLSPEC_HIDDEN;
|
extern int msi_get_property_int( MSIDATABASE *package, LPCWSTR prop, int def ) DECLSPEC_HIDDEN;
|
||||||
extern LPWSTR resolve_source_folder(MSIPACKAGE *package, LPCWSTR name, MSIFOLDER **folder) DECLSPEC_HIDDEN;
|
extern LPWSTR resolve_source_folder(MSIPACKAGE *package, LPCWSTR name, MSIFOLDER **folder) DECLSPEC_HIDDEN;
|
||||||
extern LPWSTR resolve_target_folder(MSIPACKAGE *package, LPCWSTR name, BOOL set_prop, BOOL load_prop, MSIFOLDER **folder) DECLSPEC_HIDDEN;
|
extern void msi_resolve_target_folder(MSIPACKAGE *package, const WCHAR *name, BOOL load_prop) DECLSPEC_HIDDEN;
|
||||||
|
extern void msi_clean_path( WCHAR *p ) DECLSPEC_HIDDEN;
|
||||||
extern LPWSTR resolve_file_source(MSIPACKAGE *package, MSIFILE *file) DECLSPEC_HIDDEN;
|
extern LPWSTR resolve_file_source(MSIPACKAGE *package, MSIFILE *file) DECLSPEC_HIDDEN;
|
||||||
extern const WCHAR *msi_get_target_folder(MSIPACKAGE *package, const WCHAR *name) DECLSPEC_HIDDEN;
|
extern const WCHAR *msi_get_target_folder(MSIPACKAGE *package, const WCHAR *name) DECLSPEC_HIDDEN;
|
||||||
extern void msi_reset_folders( MSIPACKAGE *package, BOOL source ) DECLSPEC_HIDDEN;
|
extern void msi_reset_folders( MSIPACKAGE *package, BOOL source ) DECLSPEC_HIDDEN;
|
||||||
|
@ -1045,6 +1052,7 @@ extern void ui_actiondata(MSIPACKAGE *, LPCWSTR, MSIRECORD *) DECLSPEC_HIDDEN;
|
||||||
static const WCHAR cszSourceDir[] = {'S','o','u','r','c','e','D','i','r',0};
|
static const WCHAR cszSourceDir[] = {'S','o','u','r','c','e','D','i','r',0};
|
||||||
static const WCHAR cszSOURCEDIR[] = {'S','O','U','R','C','E','D','I','R',0};
|
static const WCHAR cszSOURCEDIR[] = {'S','O','U','R','C','E','D','I','R',0};
|
||||||
static const WCHAR cszRootDrive[] = {'R','O','O','T','D','R','I','V','E',0};
|
static const WCHAR cszRootDrive[] = {'R','O','O','T','D','R','I','V','E',0};
|
||||||
|
static const WCHAR cszTargetDir[] = {'T','A','R','G','E','T','D','I','R',0};
|
||||||
static const WCHAR szLocalSid[] = {'S','-','1','-','5','-','1','8',0};
|
static const WCHAR szLocalSid[] = {'S','-','1','-','5','-','1','8',0};
|
||||||
static const WCHAR szEmpty[] = {0};
|
static const WCHAR szEmpty[] = {0};
|
||||||
static const WCHAR szAll[] = {'A','L','L',0};
|
static const WCHAR szAll[] = {'A','L','L',0};
|
||||||
|
|
|
@ -90,6 +90,26 @@ static void free_feature( MSIFEATURE *feature )
|
||||||
msi_free( feature );
|
msi_free( feature );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void free_folder( MSIFOLDER *folder )
|
||||||
|
{
|
||||||
|
struct list *item, *cursor;
|
||||||
|
|
||||||
|
LIST_FOR_EACH_SAFE( item, cursor, &folder->children )
|
||||||
|
{
|
||||||
|
FolderList *fl = LIST_ENTRY( item, FolderList, entry );
|
||||||
|
list_remove( &fl->entry );
|
||||||
|
msi_free( fl );
|
||||||
|
}
|
||||||
|
msi_free( folder->Parent );
|
||||||
|
msi_free( folder->Directory );
|
||||||
|
msi_free( folder->TargetDefault );
|
||||||
|
msi_free( folder->SourceLongPath );
|
||||||
|
msi_free( folder->SourceShortPath );
|
||||||
|
msi_free( folder->ResolvedTarget );
|
||||||
|
msi_free( folder->ResolvedSource );
|
||||||
|
msi_free( folder );
|
||||||
|
}
|
||||||
|
|
||||||
static void free_extension( MSIEXTENSION *ext )
|
static void free_extension( MSIEXTENSION *ext )
|
||||||
{
|
{
|
||||||
struct list *item, *cursor;
|
struct list *item, *cursor;
|
||||||
|
@ -140,17 +160,8 @@ static void free_package_structures( MSIPACKAGE *package )
|
||||||
LIST_FOR_EACH_SAFE( item, cursor, &package->folders )
|
LIST_FOR_EACH_SAFE( item, cursor, &package->folders )
|
||||||
{
|
{
|
||||||
MSIFOLDER *folder = LIST_ENTRY( item, MSIFOLDER, entry );
|
MSIFOLDER *folder = LIST_ENTRY( item, MSIFOLDER, entry );
|
||||||
|
|
||||||
list_remove( &folder->entry );
|
list_remove( &folder->entry );
|
||||||
msi_free( folder->Parent );
|
free_folder( folder );
|
||||||
msi_free( folder->Directory );
|
|
||||||
msi_free( folder->TargetDefault );
|
|
||||||
msi_free( folder->SourceLongPath );
|
|
||||||
msi_free( folder->SourceShortPath );
|
|
||||||
msi_free( folder->ResolvedTarget );
|
|
||||||
msi_free( folder->ResolvedSource );
|
|
||||||
msi_free( folder->Property );
|
|
||||||
msi_free( folder );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LIST_FOR_EACH_SAFE( item, cursor, &package->components )
|
LIST_FOR_EACH_SAFE( item, cursor, &package->components )
|
||||||
|
|
|
@ -1164,6 +1164,14 @@ static void test_settargetpath(void)
|
||||||
ok( r == ERROR_SUCCESS, "failed to get target path: %d\n", r);
|
ok( r == ERROR_SUCCESS, "failed to get target path: %d\n", r);
|
||||||
ok( !lstrcmpi(buffer, "C:\\one\\two\\three\\"), "Expected C:\\one\\two\\three\\, got %s\n", buffer);
|
ok( !lstrcmpi(buffer, "C:\\one\\two\\three\\"), "Expected C:\\one\\two\\three\\, got %s\n", buffer);
|
||||||
|
|
||||||
|
r = MsiSetTargetPath( hpkg, "TestParent", "C:\\\\one\\\\two " );
|
||||||
|
ok( r == ERROR_SUCCESS, "MsiSetTargetPath returned %d\n", r );
|
||||||
|
|
||||||
|
sz = sizeof buffer - 1;
|
||||||
|
r = MsiGetTargetPath( hpkg, "TestParent", buffer, &sz );
|
||||||
|
ok( r == ERROR_SUCCESS, "failed to get target path: %d\n", r);
|
||||||
|
ok( !lstrcmpi(buffer, "C:\\one\\two\\"), "Expected \"C:\\one\\two\\\", got %s\n", buffer);
|
||||||
|
|
||||||
MsiCloseHandle( hpkg );
|
MsiCloseHandle( hpkg );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue