/* This file is part of the KDE libraries SPDX-FileCopyrightText: 2008 Christian Ehrlicher SPDX-License-Identifier: LGPL-2.0-or-later */ #include "kmemfile_p.h" #ifndef QT_NO_SHAREDMEMORY #include #include #include #include #include class KMemFile::Private { public: struct sharedInfoData { int shmCounter; qint64 shmDataSize; sharedInfoData() { memset(this, 0, sizeof(*this)); } }; 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(QCoreApplication::translate("", "File %1 does not exist").arg(filename)); return false; } if (!f.open(QIODevice::ReadOnly)) { close(); parent->setErrorString(QCoreApplication::translate("", "Cannot open %1 for reading").arg(filename)); return false; } sharedInfoData *infoPtr = static_cast(shmInfo.data()); infoPtr->shmDataSize = f.size(); shmData.setKey(getShmKey(infoPtr->shmCounter)); if (!shmData.create(infoPtr->shmDataSize)) { close(); parent->setErrorString(QCoreApplication::translate("", "Cannot create memory segment for file %1").arg(filename)); return false; } shmData.lock(); qint64 size = 0; qint64 bytesRead; char *data = static_cast(shmData.data()); bytesRead = f.read(data, infoPtr->shmDataSize); if (bytesRead != infoPtr->shmDataSize) { close(); parent->setErrorString(QCoreApplication::translate("", "Could not read data from %1 into shm").arg(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(); } 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(QCoreApplication::translate("", "Only 'ReadOnly' allowed")); return false; } if (!QFile::exists(d->filename)) { setErrorString(QCoreApplication::translate("", "File %1 does not exist").arg(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(QCoreApplication::translate("", "Cannot create memory segment for file %1").arg(d->filename)); return false; } d->shmInfo.lock(); // no -> create it infoPtr = static_cast(d->shmInfo.data()); memset(infoPtr, 0, sizeof(Private::sharedInfoData)); infoPtr->shmCounter = 1; if (!d->loadContentsFromFile()) { d->shmInfo.unlock(); d->shmInfo.detach(); lock.unlock(); return false; } } else { d->shmInfo.lock(); infoPtr = static_cast(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(QCoreApplication::translate("", "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(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(shmData.data()); infoPtr->shmCounter++; infoPtr->shmDataSize = 0; shmData.unlock(); } #endif // QT_NO_SHAREDMEMORY #include "moc_kmemfile_p.cpp"