MDEV-27335 Windows, MSI - Bring the datadir location into the instance config UI

This commit is contained in:
Vladislav Vaintroub 2021-12-21 12:24:35 +01:00
parent 3712808ad2
commit 2a6e086902
3 changed files with 96 additions and 63 deletions

View file

@ -161,70 +161,100 @@ static void EscapeCommandLine(const wchar_t *in, wchar_t *out, size_t buflen)
}
out[pos++]= 0;
}
/*
Check for if directory is empty during install,
sets "<PROPERTY>_NOT_EMPTY" otherise
*/
extern "C" UINT __stdcall CheckDirectoryEmpty(MSIHANDLE hInstall,
const wchar_t *PropertyName)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
wchar_t buf[MAX_PATH];
DWORD len = MAX_PATH;
bool IsDirectoryEmptyOrNonExisting(const wchar_t *dir) {
wchar_t wildcard[MAX_PATH+3];
WIN32_FIND_DATAW data;
HANDLE h;
bool empty;
wcscpy_s(wildcard, MAX_PATH, dir);
wcscat_s(wildcard, MAX_PATH, L"*.*");
bool empty= true;
h= FindFirstFile(wildcard, &data);
if (h != INVALID_HANDLE_VALUE)
{
for (;;)
{
if (wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L".."))
{
empty= false;
break;
}
if (!FindNextFile(h, &data))
break;
}
FindClose(h);
}
return empty;
}
hr = WcaInitialize(hInstall, __FUNCTION__);
/*
Check for valid data directory is empty during install
A valid data directory is non-existing, or empty.
In addition, it must be different from any directories that
are going to be installed. This is required. because the full
directory is removed on a feature uninstall, and we do not want
it to be lib or bin.
*/
extern "C" UINT __stdcall CheckDataDirectory(MSIHANDLE hInstall)
{
HRESULT hr= S_OK;
UINT er= ERROR_SUCCESS;
wchar_t datadir[MAX_PATH];
DWORD len= MAX_PATH;
bool empty;
wchar_t *path= 0;
MsiGetPropertyW(hInstall, L"DATADIR", datadir, &len);
hr= WcaInitialize(hInstall, __FUNCTION__);
ExitOnFailure(hr, "Failed to initialize");
WcaLog(LOGMSG_STANDARD, "Initialized.");
MsiGetPropertyW(hInstall, PropertyName, buf, &len);
wcscat_s(buf, MAX_PATH, L"*.*");
WcaLog(LOGMSG_STANDARD, "Checking files in %S", buf);
h= FindFirstFile(buf, &data);
if (h != INVALID_HANDLE_VALUE)
{
empty= true;
for(;;)
{
if (wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L".."))
{
empty= false;
break;
}
if (!FindNextFile(h, &data))
break;
}
FindClose(h);
}
WcaLog(LOGMSG_STANDARD, "Checking files in %S", datadir);
empty= IsDirectoryEmptyOrNonExisting(datadir);
if (empty)
WcaLog(LOGMSG_STANDARD, "DATADIR is empty or non-existent");
else
WcaLog(LOGMSG_STANDARD, "DATADIR is NOT empty");
if (!empty)
{
/* Non-existent directory, we handle it as empty */
empty = true;
WcaSetProperty(L"DATADIRERROR", L"data directory exist and not empty");
goto LExit;
}
WcaSetProperty(L"DATADIRERROR", L"");
if(empty)
WcaLog(LOGMSG_STANDARD, "Directory %S is empty or non-existent",
PropertyName);
else
WcaLog(LOGMSG_STANDARD, "Directory %S is NOT empty", PropertyName);
wcscpy_s(buf, MAX_PATH, PropertyName);
wcscat_s(buf, L"NOTEMPTY");
WcaSetProperty(buf, empty? L"":L"1");
WcaGetFormattedString(L"[INSTALLDIR]",&path);
if (path && !wcsicmp(datadir, path))
{
WcaSetProperty(L"DATADIRERROR", L"data directory can not be "
L"installation root directory");
ReleaseStr(path);
goto LExit;
}
for (auto dir :
{L"[INSTALLDIR]bin\\", L"[INSTALLDIR]include\\",
L"[INSTALLDIR]lib\\", L"[INSTALLDIR]share\\"})
{
WcaGetFormattedString(dir, &path);
if (path && !wcsnicmp(datadir, path, wcslen(path)))
{
const wchar_t *subdir= dir + sizeof("[INSTALLDIR]") - 1;
wchar_t msg[MAX_PATH]= L"data directory conflicts with '";
wcsncat_s(msg, subdir, wcslen(subdir) - 1);
wcscat_s(msg, L"' directory, which is part of this installation");
WcaSetProperty(L"DATADIRERROR", msg);
ReleaseStr(path);
goto LExit;
}
ReleaseStr(path);
path= 0;
}
LExit:
return WcaFinalize(er);
}
extern "C" UINT __stdcall CheckDataDirectoryEmpty(MSIHANDLE hInstall)
{
return CheckDirectoryEmpty(hInstall, L"DATADIR");
}
bool CheckServiceExists(const wchar_t *name)
{

View file

@ -5,6 +5,6 @@ PresetDatabaseProperties
RemoveDataDirectory
CreateDatabaseRollback
CheckDatabaseProperties
CheckDataDirectoryEmpty
CheckDataDirectory
CheckDBInUse
CheckServiceUpgrades

View file

@ -230,12 +230,22 @@
<Control Id="CheckBoxUTF8" Type="CheckBox" X="8" Y="154" Width="250" Height="18" Property="UTF8" CheckBoxValue="1" TabSkip="no">
<Text>{\Font1}Use UTF8 as default server's character set</Text>
</Control>
<Control Type="Text" Id="Text11" Width="67" Height="17" X="8" Y="190" Text="{\Font1}Data directory" />
<Control Type="PathEdit" Id="TxtDir" Width="175" Height="18" X="80" Y="190" Property="DATADIR">
</Control>
<Control Id="btnDirBrowse" Type="PushButton" Width="56" Height="17" X="278" Y="190" Text="Browse...">
<Publish Property="_BrowseProperty" Value="DATADIR" Order="1">1</Publish>
<Publish Event="SpawnDialog" Value="BrowseDlg" Order="2">1</Publish>
</Control>
<!-- Navigation buttons-->
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="&amp;Back">
<Publish Event="NewDialog" Value="CustomizeDlg">1</Publish>
</Control>
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="&amp;Next">
<Publish Event="DoAction" Value="CheckDataDirectory">1</Publish>
<Publish Property="WarningText" Value="Invalid data directory, choose a different one. Error: [DATADIRERROR]">
DATADIRERROR
</Publish>
<Publish Property="WarningText" Value="Passwords do not match."><![CDATA[PASSWORD <> RootPasswordConfirm]]></Publish>
<Publish Event="SpawnDialog" Value="WarningDlg"><![CDATA[WarningText <>""]]></Publish>
<Publish Property="SERVICENAME" Value="MariaDB">NOT SERVICENAME AND NOT WarningText</Publish>
@ -335,7 +345,7 @@
</UI>
<Property Id="CRLF" Value="&#xD;&#xA;" />
<CustomAction Id="CheckDataDirectoryEmpty" BinaryKey="wixca.dll" DllEntry="CheckDataDirectoryEmpty" Execute="immediate" Impersonate="yes"/>
<CustomAction Id="CheckDataDirectory" BinaryKey="wixca.dll" DllEntry="CheckDataDirectory" Execute="immediate" Impersonate="yes"/>
<!-- What to do when navigation buttons are clicked -->
<UI Id="MyWixUI_Mondo">
<UIRef Id="WixUI_FeatureTree" />
@ -355,13 +365,6 @@
<Publish Dialog="CustomizeDlg" Control="Back" Event="NewDialog" Value="NewOrUpgradeInstanceDlg" Order="999">
NOT Installed AND UpgradableServiceFound
</Publish>
<Publish Dialog="CustomizeDlg" Control="Next" Event="DoAction" Value="CheckDataDirectoryEmpty" Order="1"><![CDATA[&DBInstance=3 AND NOT !DBInstance=3]]></Publish>
<Publish Dialog="CustomizeDlg" Property="DATADIRNOTEMPTY" Control="Next" Order="1"><![CDATA[NOT(&DBInstance=3 AND NOT !DBInstance=3)]]></Publish>
<Publish Dialog="CustomizeDlg" Control="Next" Property="WarningText" Order="2"
Value="Selected data directory [DATADIR] is not empty. Either clean it, or choose another location for 'Database Instance' feature.">
DATADIRNOTEMPTY
</Publish>
<Publish Dialog="CustomizeDlg" Control="Next" Event="SpawnDialog" Value="WarningDlg" Order="3">WarningText</Publish>
<Publish Dialog="CustomizeDlg" Control="Next" Event="NewDialog" Value="ConfirmDataCleanupDlg" Order="4">
<![CDATA[(&DBInstance=2) AND (!DBInstance=3)]]>
</Publish>
@ -634,13 +637,13 @@
Property="FailureProgram"
Execute="deferred" />
<CustomAction Id='ErrorDataDirNotEmpty'
Error='Chosen data directory [DATADIR] is not empty. It must be empty prior to installation.'/>
<CustomAction Id='ErrorDataDir'
Error='Invalid data directory, choose a different one. Error : [DATADIRERROR]'/>
<InstallExecuteSequence>
<Custom Action="CheckDataDirectoryEmpty" After="CostFinalize">
<Custom Action="CheckDataDirectory" After="CostFinalize">
<![CDATA[&DBInstance=3 AND NOT !DBInstance=3 AND OLDERVERSIONBEINGUPGRADED=""]]>
</Custom>
<Custom Action="ErrorDataDirNotEmpty" After="CheckDataDirectoryEmpty" >DATADIRNOTEMPTY</Custom>
<Custom Action="ErrorDataDir" After="CheckDataDirectory">DATADIRERROR</Custom>
<Custom Action="CheckDatabaseProperties" Before="CreateDatabaseCommand">SERVICENAME</Custom>
<Custom Action="CreateDatabaseCommand" After="CostFinalize" >
<![CDATA[&DBInstance=3 AND NOT !DBInstance=3 AND OLDERVERSIONBEINGUPGRADED=""]]>