File indexing completed on 2024-07-14 03:55:25

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2008 Christian Ehrlicher <ch.ehrlicher@gmx.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kmemfile_p.h"
0009 
0010 #ifndef QT_NO_SHAREDMEMORY
0011 
0012 #include <QCoreApplication>
0013 #include <QCryptographicHash>
0014 #include <QDir>
0015 #include <QFile>
0016 #include <QSharedMemory>
0017 
0018 class KMemFile::Private
0019 {
0020 public:
0021     struct sharedInfoData {
0022         int shmCounter;
0023         qint64 shmDataSize;
0024 
0025         sharedInfoData()
0026         {
0027             memset(this, 0, sizeof(*this));
0028         }
0029     };
0030     Private(KMemFile *_parent)
0031         : readWritePos(0)
0032         , shmDataSize(0)
0033         , parent(_parent)
0034     {
0035     }
0036 
0037     QString getShmKey(int iCounter = -1);
0038     static QString getShmKey(const QString &filename, int iCounter = -1);
0039     bool loadContentsFromFile();
0040     void close();
0041 
0042     QString filename;
0043     QSharedMemory shmInfo;
0044     QSharedMemory shmData;
0045     qint64 readWritePos;
0046     qint64 shmDataSize;
0047 
0048     KMemFile *parent;
0049 };
0050 
0051 QString KMemFile::Private::getShmKey(int iCounter)
0052 {
0053     return getShmKey(filename, iCounter);
0054 }
0055 
0056 QString KMemFile::Private::getShmKey(const QString &filename, int iCounter)
0057 {
0058     QByteArray tmp = QString(QDir(filename).canonicalPath() + QString::number(iCounter)).toUtf8();
0059     return QString::fromLatin1(QCryptographicHash::hash(tmp, QCryptographicHash::Sha1));
0060 }
0061 
0062 bool KMemFile::Private::loadContentsFromFile()
0063 {
0064     QFile f(filename);
0065     if (!f.exists()) {
0066         close();
0067         parent->setErrorString(QCoreApplication::translate("", "File %1 does not exist").arg(filename));
0068         return false;
0069     }
0070     if (!f.open(QIODevice::ReadOnly)) {
0071         close();
0072         parent->setErrorString(QCoreApplication::translate("", "Cannot open %1 for reading").arg(filename));
0073         return false;
0074     }
0075 
0076     sharedInfoData *infoPtr = static_cast<sharedInfoData *>(shmInfo.data());
0077 
0078     infoPtr->shmDataSize = f.size();
0079     shmData.setKey(getShmKey(infoPtr->shmCounter));
0080     if (!shmData.create(infoPtr->shmDataSize)) {
0081         close();
0082         parent->setErrorString(QCoreApplication::translate("", "Cannot create memory segment for file %1").arg(filename));
0083         return false;
0084     }
0085     shmData.lock();
0086     qint64 size = 0;
0087     qint64 bytesRead;
0088     char *data = static_cast<char *>(shmData.data());
0089     bytesRead = f.read(data, infoPtr->shmDataSize);
0090     if (bytesRead != infoPtr->shmDataSize) {
0091         close();
0092         parent->setErrorString(QCoreApplication::translate("", "Could not read data from %1 into shm").arg(filename));
0093         return false;
0094     }
0095     shmDataSize = size;
0096     shmData.unlock();
0097     return true;
0098 }
0099 
0100 void KMemFile::Private::close()
0101 {
0102     shmData.unlock();
0103     shmData.detach();
0104     shmInfo.unlock();
0105     shmInfo.detach();
0106     readWritePos = 0;
0107     shmDataSize = 0;
0108 }
0109 
0110 KMemFile::KMemFile(const QString &filename, QObject *parent)
0111     : QIODevice(parent)
0112     , d(new Private(this))
0113 {
0114     d->filename = filename;
0115 }
0116 
0117 KMemFile::~KMemFile()
0118 {
0119     close();
0120 }
0121 
0122 void KMemFile::close()
0123 {
0124     QIODevice::close();
0125     if (!isOpen()) {
0126         return;
0127     }
0128     d->close();
0129 }
0130 
0131 bool KMemFile::isSequential() const
0132 {
0133     return false;
0134 }
0135 
0136 bool KMemFile::open(OpenMode mode)
0137 {
0138     if (isOpen()) {
0139         QIODevice::open(mode);
0140         return false;
0141     }
0142 
0143     if (mode != QIODevice::ReadOnly) {
0144         setErrorString(QCoreApplication::translate("", "Only 'ReadOnly' allowed"));
0145         return false;
0146     }
0147 
0148     if (!QFile::exists(d->filename)) {
0149         setErrorString(QCoreApplication::translate("", "File %1 does not exist").arg(d->filename));
0150         return false;
0151     }
0152 
0153     QSharedMemory lock(QDir(d->filename).canonicalPath());
0154     lock.lock();
0155 
0156     Private::sharedInfoData *infoPtr;
0157     d->shmInfo.setKey(d->getShmKey());
0158     // see if it's already in memory
0159     if (!d->shmInfo.attach(QSharedMemory::ReadWrite)) {
0160         if (!d->shmInfo.create(sizeof(Private::sharedInfoData))) {
0161             lock.unlock();
0162             setErrorString(QCoreApplication::translate("", "Cannot create memory segment for file %1").arg(d->filename));
0163             return false;
0164         }
0165         d->shmInfo.lock();
0166         // no -> create it
0167         infoPtr = static_cast<Private::sharedInfoData *>(d->shmInfo.data());
0168         memset(infoPtr, 0, sizeof(Private::sharedInfoData));
0169         infoPtr->shmCounter = 1;
0170         if (!d->loadContentsFromFile()) {
0171             d->shmInfo.unlock();
0172             d->shmInfo.detach();
0173             lock.unlock();
0174             return false;
0175         }
0176     } else {
0177         d->shmInfo.lock();
0178         infoPtr = static_cast<Private::sharedInfoData *>(d->shmInfo.data());
0179         d->shmData.setKey(d->getShmKey(infoPtr->shmCounter));
0180         if (!d->shmData.attach(QSharedMemory::ReadOnly)) {
0181             if (!d->loadContentsFromFile()) {
0182                 d->shmInfo.unlock();
0183                 d->shmInfo.detach();
0184                 lock.unlock();
0185                 return false;
0186             }
0187         }
0188     }
0189     d->shmDataSize = infoPtr->shmDataSize;
0190     d->shmInfo.unlock();
0191     lock.unlock();
0192 
0193     setOpenMode(mode);
0194     return true;
0195 }
0196 
0197 bool KMemFile::seek(qint64 pos)
0198 {
0199     if (d->shmDataSize < pos) {
0200         setErrorString(QCoreApplication::translate("", "Cannot seek past eof"));
0201         return false;
0202     }
0203     d->readWritePos = pos;
0204     QIODevice::seek(pos);
0205     return true;
0206 }
0207 
0208 qint64 KMemFile::size() const
0209 {
0210     return d->shmDataSize;
0211 }
0212 
0213 qint64 KMemFile::readData(char *data, qint64 maxSize)
0214 {
0215     if ((openMode() & QIODevice::ReadOnly) == 0) {
0216         return -1;
0217     }
0218 
0219     qint64 maxRead = size() - d->readWritePos;
0220     qint64 bytesToRead = qMin(maxRead, maxSize);
0221     const char *src = static_cast<const char *>(d->shmData.data());
0222     memcpy(data, &src[d->readWritePos], bytesToRead);
0223     d->readWritePos += bytesToRead;
0224     return bytesToRead;
0225 }
0226 
0227 qint64 KMemFile::writeData(const char *, qint64)
0228 {
0229     return -1;
0230 }
0231 
0232 void KMemFile::fileContentsChanged(const QString &filename)
0233 {
0234     QSharedMemory lock(QDir(filename).canonicalPath());
0235     lock.lock();
0236 
0237     QSharedMemory shmData(Private::getShmKey(filename));
0238     if (!shmData.attach()) {
0239         return;
0240     }
0241     shmData.lock();
0242     Private::sharedInfoData *infoPtr = static_cast<Private::sharedInfoData *>(shmData.data());
0243     infoPtr->shmCounter++;
0244     infoPtr->shmDataSize = 0;
0245     shmData.unlock();
0246 }
0247 
0248 #endif // QT_NO_SHAREDMEMORY
0249 
0250 #include "moc_kmemfile_p.cpp"