Enable opening of downloaded fonts only in ForKit in Online

We want that only the ForKit process needs to have access to new font
files added to a Collabora Online instance dynamically by downloading
from a server. There are however many locations in the Kit process, in
core and in external libraries like harfbuzz, where the code wants to
open a font file.

Handle this so that the ForKit process opens such a downloaded font
file and doesn't close it. The file descriptor is thus inherited by
Kit processes.  The font file pathname passed on to other code is a
fake on in the format "/:FD:/%d" where the %d is the file descriptor
of the opened font file. Add checks in all places where font files are
opened, look for this special pathname format, and modify the code to
just dup() the already open file descriptor in that case.

All this is relevant for Linux only, as Collabora Online runs on
Linux.

Do the above for harfbuzz, cairo, fontconfig, and freetype.

In addition make sure that these libraries (except harfbuzz which
needs to be a static library and freetype) when bundled, on Linux, are
built as shared libraries, and won't be confused with the
corresponding system libraries by making sure their sonames are
different.

Change-Id: Ib059cb27e1637d07bb709249abd0d984f948caa9
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/140714
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com>
Reviewed-by: Tor Lillqvist <tml@collabora.com>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146341
Tested-by: Jenkins
This commit is contained in:
Tor Lillqvist 2022-09-20 16:07:14 +03:00
parent 1854f8ae2c
commit d552b4a549
19 changed files with 270 additions and 48 deletions

View file

@ -10823,11 +10823,18 @@ dnl ===================================================================
GRAPHITE_CFLAGS_internal="-I${WORKDIR}/UnpackedTarball/graphite/include -DGRAPHITE2_STATIC"
GRAPHITE_LIBS_internal="-L${WORKDIR}/LinkTarget/StaticLibrary -lgraphite"
libo_CHECK_SYSTEM_MODULE([graphite],[GRAPHITE],[graphite2 >= 0.9.3])
HARFBUZZ_CFLAGS_internal="-I${WORKDIR}/UnpackedTarball/harfbuzz/src"
HARFBUZZ_LIBS_internal="-L${WORKDIR}/UnpackedTarball/harfbuzz/src/.libs -lharfbuzz"
libo_CHECK_SYSTEM_MODULE([harfbuzz],[HARFBUZZ],[harfbuzz-icu >= 2.6.8])
case "$_os" in
Linux)
libo_CHECK_SYSTEM_MODULE([graphite],[GRAPHITE],[graphite2 >= 0.9.3],,,TRUE)
libo_CHECK_SYSTEM_MODULE([harfbuzz],[HARFBUZZ],[harfbuzz-icu >= 0.9.42],,,TRUE)
;;
*)
libo_CHECK_SYSTEM_MODULE([graphite],[GRAPHITE],[graphite2 >= 0.9.3])
libo_CHECK_SYSTEM_MODULE([harfbuzz],[HARFBUZZ],[harfbuzz-icu >= 0.9.42])
;;
esac
if test "$COM" = "MSC"; then # override the above
GRAPHITE_LIBS="${WORKDIR}/LinkTarget/StaticLibrary/graphite.lib"

View file

@ -26,6 +26,10 @@
#include <postmac.h>
#endif
#ifdef LINUX
#include <fcntl.h>
#endif
#ifdef ANDROID
#include <osl/detail/android-bootstrap.h>
#endif
@ -4554,13 +4558,27 @@ static void lo_setOption(LibreOfficeKit* /*pThis*/, const char *pOption, const c
else
sal_detail_set_log_selector(pCurrentSalLogOverride);
}
#ifdef LINUX
else if (strcmp(pOption, "addfont") == 0)
{
if (memcmp(pValue, "file://", 7) == 0)
pValue += 7;
int fd = open(pValue, O_RDONLY);
if (fd == -1)
{
std::cerr << "Could not open font file '" << pValue << "': " << strerror(errno) << std::endl;
return;
}
OUString sMagicFileName = "file:///:FD:/" + OUString::number(fd);
OutputDevice *pDevice = Application::GetDefaultDevice();
OutputDevice::ImplClearAllFontData(false);
pDevice->AddTempDevFont(OUString::fromUtf8(pValue), "");
pDevice->AddTempDevFont(sMagicFileName, "");
OutputDevice::ImplRefreshAllFontData(false);
}
#endif
}
static void lo_dumpState (LibreOfficeKit* pThis, const char* /* pOptions */, char** pState)

View file

@ -12,7 +12,8 @@ $(eval $(call gb_ExternalPackage_ExternalPackage,cairo,cairo))
$(eval $(call gb_ExternalPackage_use_external_project,cairo,cairo))
ifneq ($(DISABLE_DYNLOADING),TRUE)
$(eval $(call gb_ExternalPackage_add_file,cairo,$(LIBO_LIB_FOLDER)/libcairo.so.2,src/.libs/libcairo.so.2.1170$(CAIRO_VERSION_MICRO).0))
$(eval $(call gb_ExternalPackage_add_file,cairo,$(LIBO_LIB_FOLDER)/libcairo-lo.so.2.1170$(CAIRO_VERSION_MICRO).0,src/.libs/libcairo-lo.so.2.1170$(CAIRO_VERSION_MICRO).0))
$(eval $(call gb_ExternalPackage_add_file,cairo,$(LIBO_LIB_FOLDER)/libcairo-lo.so.2,src/.libs/libcairo-lo.so.2))
endif
# vim: set noet sw=4 ts=4:

View file

@ -28,6 +28,8 @@ $(eval $(call gb_UnpackedTarball_add_patches,cairo,\
external/cairo/cairo/cairo.ofz46165.patch.1 \
external/cairo/cairo/cairo.ofz50805.patch.1 \
external/cairo/cairo/0025-libtool-pass-use-ld.patch \
external/cairo/cairo/libcairo-bundled-soname.patch.0 \
external/cairo/cairo/cairo-fd-hack.patch.0 \
))
ifeq ($(OS),iOS)

View file

@ -0,0 +1,15 @@
# -*- Mode: Diff -*-
--- src/cairo-ft-font.c
+++ src/cairo-ft-font.c
@@ -737,7 +737,10 @@
if (ret == FcResultOutOfMemory)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
if (ret == FcResultMatch) {
- if (access (filename, R_OK) == 0) {
+ int nFD = -1;
+ int n;
+ if ((sscanf (filename, "/:FD:/%d%n", &nFD, &n) == 1 && filename[n] == '\0') ||
+ access (filename, R_OK) == 0) {
/* If FC_INDEX is not set, we just use 0 */
ret = FcPatternGetInteger (pattern, FC_INDEX, 0, &id);
if (ret == FcResultOutOfMemory)

View file

@ -0,0 +1,12 @@
# -*- Mode: Diff -*-
--- src/Makefile.in
+++ src/Makefile.in
@@ -2075,7 +2075,7 @@
$(enabled_cairo_sources) \
$(NULL)
-libcairo_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(CAIRO_LIBTOOL_VERSION_INFO) -no-undefined $(export_symbols)
+libcairo_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(CAIRO_LIBTOOL_VERSION_INFO) -no-undefined $(export_symbols) -release lo -Wl,-soname -Wl,libcairo-lo.so.2
libcairo_la_LIBADD = $(CAIRO_LIBS)
libcairo_la_DEPENDENCIES = $(cairo_def_dependency)

View file

@ -0,0 +1,16 @@
# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
#
# This file is part of the LibreOffice project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
$(eval $(call gb_ExternalPackage_ExternalPackage,fontconfig,fontconfig))
$(eval $(call gb_ExternalPackage_use_external_project,fontconfig,fontconfig))
$(eval $(call gb_ExternalPackage_add_file,fontconfig,$(LIBO_LIB_FOLDER)/libfontconfig-lo.so.1.12.0,src/.libs/libfontconfig-lo.so.1.12.0))
# vim: set noet sw=4 ts=4:

View file

@ -18,6 +18,9 @@ $(eval $(call gb_ExternalProject_register_targets,fontconfig,\
build \
))
# Can't have this inside the $(call gb_ExternalProject_run as it contains commas
fontconfig_add_fonts=/usr/share/X11/fonts/Type1,/usr/share/X11/fonts/TTF,/usr/local/share/fonts
$(call gb_ExternalProject_get_state_target,fontconfig,build) :
$(call gb_Trace_StartRange,fontconfig,EXTERNAL)
$(call gb_ExternalProject_run,build,\
@ -26,23 +29,31 @@ $(call gb_ExternalProject_get_state_target,fontconfig,build) :
$(gb_VISIBILITY_FLAGS) \
$(if $(filter EMSCRIPTEN,$(OS)),-pthread)" \
$(if $(filter ANDROID,$(OS)),LIBS="-lm") \
LDFLAGS="$(call gb_ExternalProject_get_link_flags,fontconfig)" \
$(if $(filter EMSCRIPTEN,$(OS)),LIBXML2_CFLAGS="$(LIBXML_CFLAGS)" LIBXML2_LIBS="$(LIBXML_LIBS)") \
$(gb_RUN_CONFIGURE) ./configure \
--disable-shared \
--disable-silent-rules \
--with-pic \
$(if $(filter ANDROID,$(OS)),--with-arch=arm) \
--with-expat-includes=$(call gb_UnpackedTarball_get_dir,expat)/lib \
--with-expat-lib=$(gb_StaticLibrary_WORKDIR) \
$(gb_CONFIGURE_PLATFORMS) \
$(if $(filter ANDROID,$(OS)), \
--disable-shared \
) \
$(if $(filter EMSCRIPTEN,$(OS)), \
--disable-shared \
--with-baseconfigdir=/instdir/share/fontconfig \
--with-cache-dir=/instdir/share/fontconfig/cache \
--with-add-fonts=/instdir/share/fonts \
--enable-libxml2 \
ac_cv_func_fstatfs=no ac_cv_func_fstatvfs=no \
) \
$(if $(filter LINUX,$(OS)), \
--disable-static \
--prefix=/ \
--with-add-fonts=$(fontconfig_add_fonts) \
--with-cache-dir=/usr/lib/fontconfig/cache \
) \
&& $(MAKE) -C src && $(MAKE) fonts.conf \
)
$(call gb_Trace_EndRange,fontconfig,EXTERNAL)

View file

@ -12,6 +12,7 @@ $(eval $(call gb_Module_Module,fontconfig))
$(eval $(call gb_Module_add_targets,fontconfig,\
ExternalProject_fontconfig \
$(if $(filter EMSCRIPTEN,$(OS)),ExternalPackage_fontconfig_data) \
$(if $(filter LINUX,$(OS)),ExternalPackage_fontconfig) \
UnpackedTarball_fontconfig \
))

View file

@ -16,6 +16,7 @@ $(eval $(call gb_UnpackedTarball_set_patchlevel,fontconfig,0))
$(eval $(call gb_UnpackedTarball_add_patches,fontconfig,\
external/fontconfig/fontconfig-2.12.1.patch.1 \
external/fontconfig/ubsan.patch \
external/fontconfig/libfontconfig-bundled-soname.patch.0 \
))
# vim: set noet sw=4 ts=4:

View file

@ -0,0 +1,11 @@
# -*- Mode: Diff -*-
--- src/Makefile.in
+++ src/Makefile.in
@@ -525,6 +525,6 @@
lib_LTLIBRARIES = libfontconfig.la
libfontconfig_la_LDFLAGS = \
- -version-info @LIBT_VERSION_INFO@ -no-undefined $(export_symbols)
+ -version-info @LIBT_VERSION_INFO@ -no-undefined $(export_symbols) -release lo -Wl,-soname -Wl,libfontconfig-lo.so.1.12.0
libfontconfig_la_LIBADD = $(ICONV_LIBS) $(FREETYPE_LIBS) $(LIBXML2_LIBS) $(EXPAT_LIBS) $(LTLIBINTL)
libfontconfig_la_DEPENDENCIES = $(fontconfig_def_dependency)

View file

@ -28,9 +28,9 @@ $(call gb_ExternalProject_get_state_target,freetype,build) :
$(gb_CONFIGURE_PLATFORMS) \
CFLAGS="$(CFLAGS) \
$(call gb_ExternalProject_get_build_flags,freetype) \
$(call gb_ExternalProject_get_link_flags,freetype) \
$(gb_VISIBILITY_FLAGS) \
$(gb_EMSCRIPTEN_CPPFLAGS)" \
LDFLAGS="$(call gb_ExternalProject_get_link_flags,freetype)" \
&& $(MAKE) install \
&& touch $@ )
$(call gb_Trace_EndRange,freetype,EXTERNAL)

View file

@ -14,6 +14,7 @@ $(eval $(call gb_UnpackedTarball_set_tarball,freetype,$(FREETYPE_TARBALL),,freet
$(eval $(call gb_UnpackedTarball_add_patches,freetype,\
external/freetype/freetype-2.6.5.patch.1 \
external/freetype/ubsan.patch \
external/freetype/freetype-fd-hack.patch.0 \
))
$(eval $(call gb_UnpackedTarball_set_patchlevel,freetype,0))

View file

@ -0,0 +1,53 @@
# -*- Mode: Diff -*-
--- src/base/ftsystem.c
+++ src/base/ftsystem.c
@@ -237,6 +237,8 @@
const char* filepathname )
{
FT_FILE* file;
+ int nFD;
+ int n;
if ( !stream )
@@ -257,7 +257,13 @@
stream->read = NULL;
stream->close = NULL;
- file = ft_fopen( filepathname, "rb" );
+ if ( sscanf( filepathname, "/:FD:/%d%n", &nFD, &n ) == 1 && filepathname[n] == '\0')
+ {
+ lseek( nFD, 0, SEEK_SET );
+ file = fdopen( dup( nFD ), "rb" );
+ }
+ else
+ file = ft_fopen( filepathname, "rb" );
if ( !file )
{
FT_ERROR(( "FT_Stream_Open:"
--- builds/unix/ftsystem.c
+++ builds/unix/ftsystem.c
@@ -249,6 +249,8 @@
{
int file;
struct stat stat_buf;
+ int nFD;
+ int n;
if ( !stream )
@@ -255,7 +255,13 @@
return FT_THROW( Invalid_Stream_Handle );
/* open the file */
- file = open( filepathname, O_RDONLY );
+ if ( sscanf( filepathname, "/:FD:/%d%n", &nFD, &n ) == 1 && filepathname[n] == '\0')
+ {
+ lseek( nFD, 0, SEEK_SET );
+ file = dup( nFD );
+ }
+ else
+ file = open( filepathname, O_RDONLY );
if ( file < 0 )
{
FT_ERROR(( "FT_Stream_Open:" ));

View file

@ -16,6 +16,9 @@ $(eval $(call gb_UnpackedTarball_update_autoconf_configs,harfbuzz))
$(eval $(call gb_UnpackedTarball_set_patchlevel,harfbuzz,0))
$(eval $(call gb_UnpackedTarball_add_patches,harfbuzz, \
$(if $(filter LINUX,$(OS)), \
external/harfbuzz/harfbuzz-fd-hack.patch.0 \
) \
))
# vim: set noet sw=4 ts=4:

View file

@ -0,0 +1,24 @@
# -*- Mode: Diff -*-
--- src/hb-blob.cc
+++ src/hb-blob.cc
@@ -737,7 +737,19 @@
char *data = (char *) hb_malloc (allocated);
if (unlikely (!data)) return nullptr;
- FILE *fp = fopen (file_name, "rb");
+ FILE *fp;
+ int nFD;
+ int n;
+ if (sscanf (file_name, "/:FD:/%d%n", &nFD, &n) == 1 && file_name[n] == '\0')
+ {
+ lseek (nFD, 0, SEEK_SET);
+ fp = fdopen (dup (nFD), "rb");
+ }
+ else
+ {
+ fp = fopen (file_name, "rb");
+ }
+
if (unlikely (!fp)) goto fread_fail_without_close;
while (!feof (fp))

View file

@ -1075,7 +1075,18 @@ int CountTTCFonts(const char* fname)
{
int nFonts = 0;
sal_uInt8 buffer[12];
FILE* fd = fopen(fname, "rb");
FILE* fd;
#ifdef LINUX
int nFD;
int n;
if (sscanf(fname, "/:FD:/%d%n", &nFD, &n) == 1 && fname[n] == '\0')
{
lseek(nFD, 0, SEEK_SET);
fd = fdopen(dup(nFD), "rb");
}
else
#endif
fd = fopen(fname, "rb");
if( fd ) {
if (fread(buffer, 1, 12, fd) == 12) {
if(GetUInt32(buffer, 0) == T_ttcf )
@ -1106,7 +1117,15 @@ SFErrCodes OpenTTFontFile(const char* fname, sal_uInt32 facenum, TrueTypeFont**
goto cleanup;
}
fd = open(fname, O_RDONLY);
int nFD;
int n;
if (sscanf(fname, "/:FD:/%d%n", &nFD, &n) == 1 && fname[n] == '\0')
{
lseek(nFD, 0, SEEK_SET);
fd = dup(nFD);
}
else
fd = open(fname, O_RDONLY);
if (fd == -1) {
ret = SFErrCodes::BadFile;

View file

@ -178,25 +178,38 @@ std::vector<PrintFontManager::PrintFont> PrintFontManager::analyzeFontFile( int
OString aFullPath = aDir + "/" + rFontFile;
// #i1872# reject unreadable files
if( access( aFullPath.getStr(), R_OK ) )
return aNewFonts;
bool bSupported = false;
if (pFormat)
bool bSupported;
bool bHack = false;
int nFD;
int n;
if (sscanf(aFullPath.getStr(), "/:FD:/%d%n", &nFD, &n) == 1 && aFullPath.getStr()[n] == '\0')
{
if (!strcmp(pFormat, "TrueType") ||
!strcmp(pFormat, "CFF"))
bSupported = true;
// Hack, pathname that actually means we will use a pre-opened file descriptor
bSupported = true;
bHack = true;
}
if (!bSupported)
else
{
OString aExt( rFontFile.copy( rFontFile.lastIndexOf( '.' )+1 ) );
if( aExt.equalsIgnoreAsciiCase("ttf")
|| aExt.equalsIgnoreAsciiCase("ttc")
|| aExt.equalsIgnoreAsciiCase("tte") // #i33947# for Gaiji support
|| aExt.equalsIgnoreAsciiCase("otf") ) // check for TTF- and PS-OpenType too
bSupported = true;
// #i1872# reject unreadable files
if( access( aFullPath.getStr(), R_OK ) )
return aNewFonts;
bSupported = false;
if (pFormat)
{
if (!strcmp(pFormat, "TrueType") ||
!strcmp(pFormat, "CFF"))
bSupported = true;
}
if (!bSupported)
{
OString aExt( rFontFile.copy( rFontFile.lastIndexOf( '.' )+1 ) );
if( aExt.equalsIgnoreAsciiCase("ttf")
|| aExt.equalsIgnoreAsciiCase("ttc")
|| aExt.equalsIgnoreAsciiCase("tte") // #i33947# for Gaiji support
|| aExt.equalsIgnoreAsciiCase("otf") ) // check for TTF- and PS-OpenType too
bSupported = true;
}
}
if (bSupported)
@ -207,33 +220,36 @@ std::vector<PrintFontManager::PrintFont> PrintFontManager::analyzeFontFile( int
{
SAL_INFO("vcl.fonts", "ttc: " << aFullPath << " contains " << nLength << " fonts");
sal_uInt64 fileSize = 0;
OUString aURL;
if (osl::File::getFileURLFromSystemPath(OStringToOUString(aFullPath, osl_getThreadTextEncoding()),
aURL) == osl::File::E_None)
if (!bHack)
{
osl::File aFile(aURL);
if (aFile.open(osl_File_OpenFlag_Read | osl_File_OpenFlag_NoLock) == osl::File::E_None)
sal_uInt64 fileSize = 0;
OUString aURL;
if (osl::File::getFileURLFromSystemPath(OStringToOUString(aFullPath, osl_getThreadTextEncoding()),
aURL) == osl::File::E_None)
{
osl::DirectoryItem aItem;
if (osl::DirectoryItem::get(aURL, aItem) == osl::File::E_None)
osl::File aFile(aURL);
if (aFile.open(osl_File_OpenFlag_Read | osl_File_OpenFlag_NoLock) == osl::File::E_None)
{
osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileSize );
if (aItem.getFileStatus(aFileStatus) == osl::File::E_None)
fileSize = aFileStatus.getFileSize();
osl::DirectoryItem aItem;
if (osl::DirectoryItem::get(aURL, aItem) == osl::File::E_None)
{
osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileSize );
if (aItem.getFileStatus(aFileStatus) == osl::File::E_None)
fileSize = aFileStatus.getFileSize();
}
}
}
}
//Feel free to calc the exact max possible number of fonts a file
//could contain given its physical size. But this will clamp it to
//a sane starting point
//http://processingjs.nihongoresources.com/the_smallest_font/
//https://github.com/grzegorzrolek/null-ttf
const int nMaxFontsPossible = fileSize / 528;
if (nLength > nMaxFontsPossible)
nLength = nMaxFontsPossible;
//Feel free to calc the exact max possible number of fonts a file
//could contain given its physical size. But this will clamp it to
//a sane starting point
//http://processingjs.nihongoresources.com/the_smallest_font/
//https://github.com/grzegorzrolek/null-ttf
const int nMaxFontsPossible = fileSize / 528;
if (nLength > nMaxFontsPossible)
nLength = nMaxFontsPossible;
}
for( int i = 0; i < nLength; i++ )
{

View file

@ -103,7 +103,16 @@ bool FreetypeFontFile::Map()
if (mnRefCount++ == 0)
{
const char* pFileName = maNativeFileName.getStr();
int nFile = open( pFileName, O_RDONLY );
int nFile;
int nFD;
int n;
if( sscanf( pFileName, "/:FD:/%d%n", &nFD, &n ) == 1 && pFileName[n] == '\0' )
{
lseek( nFD, 0, SEEK_SET );
nFile = dup( nFD );
}
else
nFile = open( pFileName, O_RDONLY );
if( nFile < 0 )
{
SAL_WARN("vcl.unx.freetype", "open('" << maNativeFileName << "') failed: " << strerror(errno));
@ -126,6 +135,8 @@ bool FreetypeFontFile::Map()
SAL_WARN("vcl.unx.freetype", "mmap of '" << maNativeFileName << "' failed: " << strerror(errno));
mpFileMap = nullptr;
}
else
SAL_INFO("vcl.unx.freetype", "mmap'ed '" << maNativeFileName << "' successfully");
close( nFile );
}