generic: use file strategy for ksycoca by default

fixes thread-safety issues such as KRunner crashes

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2021-01-22 20:48:03 +02:00
parent 077f3395a4
commit ddfc92dc1e
8 changed files with 2 additions and 386 deletions

View file

@ -231,7 +231,6 @@ set(kdecore_LIB_SRCS
sycoca/ksycocafactory.cpp
sycoca/kprotocolinfo.cpp
sycoca/kprotocolinfofactory.cpp
sycoca/kmemfile.cpp
text/kstringhandler.cpp
util/kautostart.cpp
util/kdedmodule.cpp

View file

@ -139,7 +139,6 @@ sonnet/
spellerplugin.cpp
sycoca/
kmemfile.cpp
kprotocolinfo.cpp David Faure <faure@kde.org>
kprotocolinfofactory.cpp David Faure <faure@kde.org>
ksycoca.cpp David Faure <faure@kde.org>

View file

@ -1,249 +0,0 @@
/*
This file is part of the KDE libraries
Copyright (C) 2008 Christian Ehrlicher <ch.ehrlicher@gmx.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kmemfile.h"
#ifndef QT_NO_SHAREDMEMORY
#include <QtCore/QSharedMemory>
#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QCryptographicHash>
#include "klocalizedstring.h"
class KMemFile::Private
{
public:
struct sharedInfoData {
int shmCounter;
qint64 shmDataSize;
};
Private ( KMemFile *_parent ) : readWritePos ( 0 ), shmDataSize ( 0 ), parent ( _parent ) {}
QString getShmKey ( int iCounter = -1 );
static QString getShmKey ( const QString &filename, int iCounter = -1 );
bool loadContentsFromFile();
void close();
QString filename;
QSharedMemory shmInfo;
QSharedMemory shmData;
qint64 readWritePos;
qint64 shmDataSize;
KMemFile *parent;
};
QString KMemFile::Private::getShmKey ( int iCounter )
{
return getShmKey ( filename, iCounter );
}
QString KMemFile::Private::getShmKey ( const QString &filename, int iCounter )
{
QByteArray tmp = QString ( QDir ( filename ).canonicalPath() + QString::number ( iCounter ) ).toUtf8();
return QString::fromLatin1 ( QCryptographicHash::hash ( tmp, QCryptographicHash::Sha1 ) );
}
bool KMemFile::Private::loadContentsFromFile()
{
QFile f ( filename );
if ( !f.exists() ) {
close();
parent->setErrorString ( i18n ( "File %1 does not exist" , filename ) );
return false;
}
if ( !f.open ( QIODevice::ReadOnly ) ) {
close();
parent->setErrorString ( i18n ( "Cannot open %1 for reading" , filename ) );
return false;
}
sharedInfoData *infoPtr = static_cast<sharedInfoData*> ( shmInfo.data() );
infoPtr->shmDataSize = f.size();
shmData.setKey ( getShmKey ( infoPtr->shmCounter ) );
if ( !shmData.create ( infoPtr->shmDataSize ) ) {
close();
parent->setErrorString ( i18n ( "Cannot create memory segment for file %1" , filename ) );
return false;
}
shmData.lock();
qint64 size = 0;
qint64 bytesRead;
char *data = static_cast<char*> ( shmData.data() );
bytesRead = f.read ( data, infoPtr->shmDataSize );
if ( bytesRead != infoPtr->shmDataSize ) {
close();
parent->setErrorString ( i18n ( "Could not read data from %1 into shm" , filename ) );
return false;
}
shmDataSize = size;
shmData.unlock();
return true;
}
void KMemFile::Private::close()
{
shmData.unlock();
shmData.detach();
shmInfo.unlock();
shmInfo.detach();
readWritePos = 0;
shmDataSize = 0;
}
KMemFile::KMemFile ( const QString &filename, QObject *parent )
: QIODevice ( parent ), d ( new Private ( this ) )
{
d->filename = filename;
}
KMemFile::~KMemFile()
{
close();
delete d;
}
void KMemFile::close ()
{
QIODevice::close();
if ( !isOpen() )
return;
d->close();
}
bool KMemFile::isSequential () const
{
return false;
}
bool KMemFile::open ( OpenMode mode )
{
if ( isOpen() ) {
QIODevice::open ( mode );
return false;
}
if ( mode != QIODevice::ReadOnly ) {
setErrorString ( i18n ( "Only 'ReadOnly' allowed" ) );
return false;
}
if ( !QFile::exists ( d->filename ) ) {
setErrorString ( i18n ( "File %1 does not exist" , d->filename ) );
return false;
}
QSharedMemory lock ( QDir ( d->filename ).canonicalPath() );
lock.lock();
Private::sharedInfoData *infoPtr;
d->shmInfo.setKey ( d->getShmKey() );
// see if it's already in memory
if ( !d->shmInfo.attach ( QSharedMemory::ReadWrite ) ) {
if ( !d->shmInfo.create ( sizeof ( Private::sharedInfoData ) ) ) {
lock.unlock();
setErrorString ( i18n ( "Cannot create memory segment for file %1" , d->filename ) );
return false;
}
d->shmInfo.lock();
// no -> create it
infoPtr = static_cast<Private::sharedInfoData*> ( d->shmInfo.data() );
infoPtr->shmCounter = 1;
infoPtr->shmDataSize = 0;
if ( !d->loadContentsFromFile() ) {
d->shmInfo.unlock();
d->shmInfo.detach();
lock.unlock();
return false;
}
} else {
d->shmInfo.lock();
infoPtr = static_cast<Private::sharedInfoData*> ( d->shmInfo.data() );
d->shmData.setKey ( d->getShmKey ( infoPtr->shmCounter ) );
if ( !d->shmData.attach ( QSharedMemory::ReadOnly ) ) {
if ( !d->loadContentsFromFile() ) {
d->shmInfo.unlock();
d->shmInfo.detach();
lock.unlock();
return false;
}
}
}
d->shmDataSize = infoPtr->shmDataSize;
d->shmInfo.unlock();
lock.unlock();
setOpenMode ( mode );
return true;
}
bool KMemFile::seek ( qint64 pos )
{
if ( d->shmDataSize < pos ) {
setErrorString ( i18n ( "Cannot seek past eof" ) );
return false;
}
d->readWritePos = pos;
QIODevice::seek ( pos );
return true;
}
qint64 KMemFile::size () const
{
return d->shmDataSize;
}
qint64 KMemFile::readData ( char * data, qint64 maxSize )
{
if ( ( openMode() & QIODevice::ReadOnly ) == 0 )
return -1;
qint64 maxRead = size() - d->readWritePos;
qint64 bytesToRead = qMin ( maxRead, maxSize );
const char *src = static_cast<const char*> ( d->shmData.data() );
memcpy ( data, &src[d->readWritePos], bytesToRead );
d->readWritePos += bytesToRead;
return bytesToRead;
}
qint64 KMemFile::writeData ( const char *, qint64 )
{
return -1;
}
void KMemFile::fileContentsChanged ( const QString &filename )
{
QSharedMemory lock ( QDir ( filename ).canonicalPath() );
lock.lock();
QSharedMemory shmData ( Private::getShmKey ( filename ) );
if ( !shmData.attach() )
return;
shmData.lock();
Private::sharedInfoData *infoPtr = static_cast<Private::sharedInfoData*> ( shmData.data() );
infoPtr->shmCounter++;
infoPtr->shmDataSize = 0;
shmData.unlock();
}
#endif //QT_NO_SHAREDMEMORY

View file

@ -1,100 +0,0 @@
/*
This file is part of the KDE libraries
Copyright (C) 2008 Christian Ehrlicher <ch.ehrlicher@gmx.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KMEMFILE_H
#define KMEMFILE_H
#ifndef QT_NO_SHAREDMEMORY
#include <QtCore/QIODevice>
#include <kdecore_export.h>
/**
* @internal
* Simple QIODevice for QSharedMemory to keep ksycoca cache in memory only once
* The first call to open() loads the file into a shm segment. Every
* subsequent call only attaches to this segment. When the file content changed,
* you have to execute KMemFile::fileContentsChanged() to update the internal
* structures. The next call to open() creates a new shm segment. The old one
* is automatically destroyed when the last process closed KMemFile.
*/
class KDECORE_EXPORT KMemFile : public QIODevice
{
public:
/**
* ctor
*
* @param filename the file to load into memory
* @param parent our parent
*/
explicit KMemFile ( const QString &filename, QObject *parent = 0 );
/**
* dtor
*/
virtual ~KMemFile();
/**
* closes the KMemFile
*
* @reimp
*/
virtual void close ();
/**
* As KMemFile is a random access device, it returns false
*
* @reimp
*/
virtual bool isSequential () const;
/**
* @reimp
* @param mode only QIODevice::ReadOnly is accepted
*/
virtual bool open ( OpenMode mode );
/**
* Sets the current read/write position to pos
* @reimp
* @param pos the new read/write position
*/
virtual bool seek ( qint64 pos );
/**
* Returns the size of the file
* @reimp
*/
virtual qint64 size () const;
/**
* This static function updates the internal information about the file
* loaded into shared memory. The next time the file is opened, the file is
* reread from the file system.
*/
static void fileContentsChanged ( const QString &filename );
protected:
/** @reimp */
virtual qint64 readData ( char * data, qint64 maxSize );
/** @reimp */
virtual qint64 writeData ( const char * data, qint64 maxSize );
private:
class Private;
friend class Private;
Private * const d;
};
#endif //QT_NO_SHAREDMEMORY
#endif // KMEMFILE_H

View file

@ -29,7 +29,6 @@
#include "ksharedconfig.h"
#include "kdebug.h"
#include "kstandarddirs.h"
#include "kmemfile.h"
#include <QtCore/QDataStream>
#include <QtCore/QCoreApplication>
@ -79,7 +78,7 @@ KSycocaPrivate::KSycocaPrivate()
updateSig( 0 ),
m_device(0)
{
m_sycocaStrategy = StrategyMemFile;
m_sycocaStrategy = StrategyFile;
KConfigGroup config(KGlobal::config(), "KSycoca");
setStrategyFromString(config.readEntry("strategy"));
}
@ -87,8 +86,6 @@ KSycocaPrivate::KSycocaPrivate()
void KSycocaPrivate::setStrategyFromString(const QString& strategy) {
if (strategy == QLatin1String("file"))
m_sycocaStrategy = StrategyFile;
else if (strategy == QLatin1String("sharedmem"))
m_sycocaStrategy = StrategyMemFile;
else if (!strategy.isEmpty())
kWarning(7011) << "Unknown sycoca strategy:" << strategy;
}
@ -191,14 +188,6 @@ KSycocaAbstractDevice* KSycocaPrivate::device()
device = new KSycocaBufferDevice;
device->device()->open(QIODevice::ReadOnly); // can't fail
} else {
#ifndef QT_NO_SHAREDMEMORY
if (!device && m_sycocaStrategy == StrategyMemFile) {
device = new KSycocaMemFileDevice(m_databasePath);
if (!device->device()->open(QIODevice::ReadOnly)) {
delete device; device = 0;
}
}
#endif
if (!device) {
device = new KSycocaFileDevice(m_databasePath);
if (!device->device()->open(QIODevice::ReadOnly)) {

View file

@ -56,7 +56,7 @@ public:
bool readError;
quint32 timeStamp;
enum { StrategyMemFile, StrategyFile, StrategyDummyBuffer } m_sycocaStrategy;
enum { StrategyFile, StrategyDummyBuffer } m_sycocaStrategy;
QString m_databasePath;
QStringList changeList;
QString language;

View file

@ -63,25 +63,6 @@ private:
QFile* m_database;
};
#ifndef QT_NO_SHAREDMEMORY
// Reading from a KMemFile
class KSycocaMemFileDevice : public KSycocaAbstractDevice
{
public:
KSycocaMemFileDevice(const QString& path) {
m_database = new KMemFile(path);
}
~KSycocaMemFileDevice() {
delete m_database;
}
virtual QIODevice* device() {
return m_database;
}
private:
KMemFile* m_database;
};
#endif
// Reading from a dummy memory buffer
class KSycocaBufferDevice : public KSycocaAbstractDevice
{

View file

@ -53,7 +53,6 @@
#ifndef KBUILDSYCOCA_NO_KCRASH
#include <kcrash.h>
#endif
#include <kmemfile.h>
#include <stdlib.h>
#include <unistd.h>
@ -455,8 +454,6 @@ bool KBuildSycoca::recreate()
if (g_vfolder)
str << g_vfolder->allDirectories(); // Extra resource dirs
}
if (d->m_sycocaStrategy == KSycocaPrivate::StrategyMemFile)
KMemFile::fileContentsChanged(path);
return true;
}