From 09dbbe16b04add973ab04fe5937c12478e52451c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= Date: Tue, 28 Jul 2026 17:08:38 +0000 Subject: [PATCH] firebird: name the modules of a system firebird by absolute path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Debian patches firebird to resolve its plugins, intl and conf directories under the FIREBIRD variable (instead of where it was built) and we want point that variable at a private directory. But then the modules aren't loaded and no database opens. So write a replacement plugins.conf that lists the absolute path of each plugin module (and the intl conf files) with their macros expanded. each lib.so (otherwise found by default) with no entry names gets an entry of its own. Change-Id: I2696d7197394b7fe9b7739732deadd2cb2e01acc Signed-off-by: Caolán McNamara Reviewed-on: https://gerrit.collaboraoffice.com/c/core/+/7989 Tested-by: Jenkins CPCI Reviewed-by: Michael Stahl (cherry picked from commit b8407b18adf3c6d552d54d7b6657539e2769afd6) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208882 Tested-by: Jenkins Reviewed-by: Xisco Fauli --- config_host/config_firebird.h.in | 12 ++ configure.ac | 14 ++ .../source/drivers/firebird/Driver.cxx | 173 ++++++++++++++++++ 3 files changed, 199 insertions(+) diff --git a/config_host/config_firebird.h.in b/config_host/config_firebird.h.in index edfbb2df62b4..e1d7bcce6fc3 100644 --- a/config_host/config_firebird.h.in +++ b/config_host/config_firebird.h.in @@ -7,4 +7,16 @@ Settings for Firebird #define ENABLE_FIREBIRD_SDBC 0 +/* The directory a system firebird has its plugin modules in, empty for a bundled firebird or when it + could not be determined. */ +#define SYSTEM_FIREBIRD_PLUGINS_DIRECTORY "" + +/* The directory a system firebird has its international modules in, empty for a bundled firebird or + when it could not be determined. */ +#define SYSTEM_FIREBIRD_INTL_DIRECTORY "" + +/* The directory a system firebird has its own configuration files in, empty for a bundled firebird + or when it could not be determined. */ +#define SYSTEM_FIREBIRD_CONF_DIRECTORY "" + #endif diff --git a/configure.ac b/configure.ac index 3887db5c395e..dee4a99cfa69 100644 --- a/configure.ac +++ b/configure.ac @@ -11307,6 +11307,12 @@ if test "$enable_firebird_sdbc" = "yes" ; then FIREBIRD_LIBS=`$FIREBIRDCONFIG --embedlibs` FilterLibs "${FIREBIRD_LIBS}" FIREBIRD_LIBS="${filteredlibs}" + dnl The directories chosen when firebird was built. A firebird patched to resolve them + dnl under the FIREBIRD variable instead needs to be told where its plugin modules and + dnl its international modules are. + FIREBIRD_CONFDIR=`$FIREBIRDCONFIG --confdir` + FIREBIRD_PLUGINSDIR=`$FIREBIRDCONFIG --pluginsdir` + FIREBIRD_INTLDIR=`$FIREBIRDCONFIG --intldir` fi AC_MSG_RESULT([includes `$FIREBIRD_CFLAGS', libraries `$FIREBIRD_LIBS']) AC_MSG_CHECKING([Firebird version]) @@ -11328,6 +11334,14 @@ int fb_api_is_30(void) { return 0; } #endif]])],AC_MSG_RESULT([OK]),AC_MSG_ERROR([Ensure firebird 3.0.x is installed])) CFLAGS="$save_CFLAGS" fi + if test -z "$FIREBIRD_PLUGINSDIR"; then + AC_MSG_WARN([Firebird plugins directory unknown, so a firebird that looks for its plugins + under the FIREBIRD variable cannot be told where they are, and will not open + a database. Install fb_config to get the directory.]) + fi + AC_DEFINE_UNQUOTED([SYSTEM_FIREBIRD_PLUGINS_DIRECTORY],["$FIREBIRD_PLUGINSDIR"]) + AC_DEFINE_UNQUOTED([SYSTEM_FIREBIRD_INTL_DIRECTORY],["$FIREBIRD_INTLDIR"]) + AC_DEFINE_UNQUOTED([SYSTEM_FIREBIRD_CONF_DIRECTORY],["$FIREBIRD_CONFDIR"]) ENABLE_FIREBIRD_SDBC=TRUE AC_DEFINE([ENABLE_FIREBIRD_SDBC],1) elif test "$enable_database_connectivity" = no; then diff --git a/connectivity/source/drivers/firebird/Driver.cxx b/connectivity/source/drivers/firebird/Driver.cxx index 134a2ccb3eac..e8b6a1cd2ae2 100644 --- a/connectivity/source/drivers/firebird/Driver.cxx +++ b/connectivity/source/drivers/firebird/Driver.cxx @@ -82,6 +82,9 @@ constexpr OUString our_sFirebirdLibVar = u"LIBREOFFICE_FIREBIRD_LIB"_ustr; // whenever it has changed. constexpr OUString our_sDatabasesConfName = u"databases.conf"_ustr; +// The file that gives a plugin the module to load it from. +constexpr OUString our_sPluginsConfName = u"plugins.conf"_ustr; + // Put rContent in the file at rURL, discarding whatever was there before. bool writeFile(const OUString& rURL, std::string_view aContent) { @@ -109,6 +112,173 @@ bool writeFile(const OUString& rURL, std::string_view aContent) return bWholeContentWritten; } +// The whole content of the file at rURL, empty when it cannot be read. +OString readFile(const OUString& rURL) +{ + ::osl::File aFile(rURL); + if (aFile.open(osl_File_OpenFlag_Read) != ::osl::FileBase::E_None) + return OString(); + + OStringBuffer aContent; + for (;;) + { + char aBuffer[4096]; + sal_uInt64 nRead = 0; + if (aFile.read(aBuffer, sizeof(aBuffer), nRead) != ::osl::FileBase::E_None || nRead == 0) + break; + aContent.append(std::string_view(aBuffer, nRead)); + } + aFile.close(); + return aContent.makeStringAndClear(); +} + +// The directories of the firebird installation, empty for a bundled firebird, which resolves them +// where it was built. +constexpr OUString our_sSystemPluginsPath = u"" SYSTEM_FIREBIRD_PLUGINS_DIRECTORY ""_ustr; +constexpr OUString our_sSystemIntlPath = u"" SYSTEM_FIREBIRD_INTL_DIRECTORY ""_ustr; +constexpr OUString our_sSystemConfPath = u"" SYSTEM_FIREBIRD_CONF_DIRECTORY ""_ustr; + +OString toBytes(std::u16string_view rPath) +{ + return OUStringToOString(rPath, osl_getThreadTextEncoding()); +} + +// Put the directories of the installation in place of the macros that name them. rThisPath is the +// directory the file was read from, which is what the this macro means. plugins and intl under the +// root are the plugins and international module directories. +OString expandFirebirdDirectoryMacros(const OString& rContent, const OString& rThisPath) +{ + const OString sPlugins = toBytes(our_sSystemPluginsPath); + const OString sIntl = toBytes(our_sSystemIntlPath); + + OString sExpanded = rContent.replaceAll("$(this)"_ostr, rThisPath); + sExpanded = sExpanded.replaceAll("$(dir_plugins)"_ostr, sPlugins); + sExpanded = sExpanded.replaceAll("$(root)/plugins"_ostr, sPlugins); + sExpanded = sExpanded.replaceAll("$(dir_intl)"_ostr, sIntl); + sExpanded = sExpanded.replaceAll("$(root)/intl"_ostr, sIntl); + sExpanded = sExpanded.replaceAll("$(dir_conf)"_ostr, toBytes(our_sSystemConfPath)); + return sExpanded; +} + +// The file at rSourceURL with the macros expanded, empty when one is left that we have no directory +// for. A leftover macro leaves a relative path, resolved under the root we are writing for. +OString getExpandedConf(const OUString& rSourceURL, const OUString& rSourcePath) +{ + const OString sContent + = expandFirebirdDirectoryMacros(readFile(rSourceURL), toBytes(rSourcePath)); + if (sContent.indexOf("$(") == -1) + return sContent; + + SAL_WARN("connectivity.firebird", "unexpanded macro in " << rSourcePath); + return OString(); +} + +// Write a plugins.conf into the directory the FIREBIRD variable names, giving every plugin of the +// installation the absolute path of its module. A plugin no entry names is looked for by its own +// name in the plugins directory firebird resolves, which Debian patches to sit under that variable. +void writeSystemFirebirdPluginsConf(std::u16string_view rDataDirURL) +{ + if (our_sSystemPluginsPath.isEmpty()) + return; + + OUString sPluginsURL; + if (::osl::FileBase::getFileURLFromSystemPath(our_sSystemPluginsPath, sPluginsURL) + != ::osl::FileBase::E_None) + return; + + // The installation's own file names the plugins that need more than their module. + OString sSystemConf; + OUString sConfURL; + if (!our_sSystemConfPath.isEmpty() + && ::osl::FileBase::getFileURLFromSystemPath(our_sSystemConfPath, sConfURL) + == ::osl::FileBase::E_None) + { + sSystemConf = getExpandedConf(sConfURL + "/" + our_sPluginsConfName, our_sSystemConfPath); + } + + ::osl::Directory aPluginsDirectory(sPluginsURL); + if (aPluginsDirectory.open() != ::osl::FileBase::E_None) + { + SAL_WARN("connectivity.firebird", "could not read " << our_sSystemPluginsPath); + return; + } + + const OString sPluginsPath = toBytes(our_sSystemPluginsPath); + OStringBuffer aContent(sSystemConf); + ::osl::DirectoryItem aItem; + while (aPluginsDirectory.getNextItem(aItem) == ::osl::FileBase::E_None) + { + ::osl::FileStatus aStatus(osl_FileStatus_Mask_FileName); + if (aItem.getFileStatus(aStatus) != ::osl::FileBase::E_None) + continue; + + // The module libEngine13.so holds the plugin Engine13. + const OUString sName = aStatus.getFileName(); + if (!sName.startsWith("lib") || !sName.endsWith(".so") || sName.getLength() <= 6) + continue; + const OString sPlugin = toBytes(sName.subView(3, sName.getLength() - 6)); + + if (sSystemConf.indexOf(Concat2View("= " + sPlugin + " ")) != -1) + continue; + + aContent.append("\nPlugin = " + sPlugin + " {\n Module = " + sPluginsPath + "/" + sPlugin + + "\n}\n"); + } + + const OUString sURL = OUString::Concat(rDataDirURL) + "/" + our_sPluginsConfName; + const OString sContent = aContent.makeStringAndClear(); + SAL_WARN_IF(!writeFile(sURL, std::string_view(sContent)), "connectivity.firebird", + "could not write " << sURL); +} + +// Copy the conf files of the international modules into an intl subdirectory of the directory the +// FIREBIRD variable names, with the absolute path of each module in place of the macros. Without +// them only the character sets and collations built into the engine work. +void writeSystemFirebirdIntlConfs(std::u16string_view rDataDirURL) +{ + if (our_sSystemIntlPath.isEmpty()) + return; + + OUString sIntlURL; + if (::osl::FileBase::getFileURLFromSystemPath(our_sSystemIntlPath, sIntlURL) + != ::osl::FileBase::E_None) + return; + + ::osl::Directory aIntlDirectory(sIntlURL); + if (aIntlDirectory.open() != ::osl::FileBase::E_None) + { + SAL_WARN("connectivity.firebird", "could not read " << our_sSystemIntlPath); + return; + } + + const OUString sTargetURL = OUString::Concat(rDataDirURL) + "/intl"; + if (::osl::Directory::create(sTargetURL) != ::osl::FileBase::E_None) + { + SAL_WARN("connectivity.firebird", "could not create " << sTargetURL); + return; + } + + ::osl::DirectoryItem aItem; + while (aIntlDirectory.getNextItem(aItem) == ::osl::FileBase::E_None) + { + ::osl::FileStatus aStatus(osl_FileStatus_Mask_FileName); + if (aItem.getFileStatus(aStatus) != ::osl::FileBase::E_None) + continue; + + const OUString sName = aStatus.getFileName(); + if (!sName.endsWith(".conf")) + continue; + + const OString sContent = getExpandedConf(sIntlURL + "/" + sName, our_sSystemIntlPath); + if (sContent.isEmpty()) + continue; + + const OUString sURL = sTargetURL + "/" + sName; + SAL_WARN_IF(!writeFile(sURL, std::string_view(sContent)), "connectivity.firebird", + "could not write " << sURL); + } +} + // firebird takes its root directory from the FIREBIRD variable on its first use, and reads the // firebird.conf that confines it to that directory only from there. Ask firebird which directory // it settled on and treat anything but our own as unconfined. @@ -168,6 +338,9 @@ FirebirdDriver::FirebirdDriver(const css::uno::Reference< css::uno::XComponentCo OUString sDataDirPath; ::osl::FileBase::getSystemPathFromFileURL(m_firebirdDataDirectory.GetURL(), sDataDirPath); + writeSystemFirebirdPluginsConf(m_firebirdDataDirectory.GetURL()); + writeSystemFirebirdIntlConfs(m_firebirdDataDirectory.GetURL()); + #if defined(_WIN32) // firebird upper-cases the database path before the exact-match // DatabaseAccess check, so match that here.