1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2008 Christian Ehrlicher <ch.ehrlicher@gmx.de>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "kmemfile_p.h"
9
10#ifndef QT_NO_SHAREDMEMORY
11
12#include <QCoreApplication>
13#include <QCryptographicHash>
14#include <QDir>
15#include <QFile>
16#include <QSharedMemory>
17
18class KMemFile::Private
19{
20public:
21 struct sharedInfoData {
22 int shmCounter = 0;
23 qint64 shmDataSize = 0;
24
25 sharedInfoData() = default;
26 };
27 Private(KMemFile *_parent)
28 : readWritePos(0)
29 , shmDataSize(0)
30 , parent(_parent)
31 {
32 }
33
34 QString getShmKey(int iCounter = -1);
35 static QString getShmKey(const QString &filename, int iCounter = -1);
36 bool loadContentsFromFile();
37 void close();
38
39 QString filename;
40 QSharedMemory shmInfo;
41 QSharedMemory shmData;
42 qint64 readWritePos;
43 qint64 shmDataSize;
44
45 KMemFile *parent;
46};
47
48QString KMemFile::Private::getShmKey(int iCounter)
49{
50 return getShmKey(filename, iCounter);
51}
52
53QString KMemFile::Private::getShmKey(const QString &filename, int iCounter)
54{
55 QByteArray tmp = QString(QDir(filename).canonicalPath() + QString::number(iCounter)).toUtf8();
56 return QString::fromLatin1(ba: QCryptographicHash::hash(data: tmp, method: QCryptographicHash::Sha1));
57}
58
59bool KMemFile::Private::loadContentsFromFile()
60{
61 QFile f(filename);
62 if (!f.exists()) {
63 close();
64 parent->setErrorString(QCoreApplication::translate(context: "", key: "File %1 does not exist").arg(a: filename));
65 return false;
66 }
67 if (!f.open(flags: QIODevice::ReadOnly)) {
68 close();
69 parent->setErrorString(QCoreApplication::translate(context: "", key: "Cannot open %1 for reading").arg(a: filename));
70 return false;
71 }
72
73 sharedInfoData *infoPtr = static_cast<sharedInfoData *>(shmInfo.data());
74
75 infoPtr->shmDataSize = f.size();
76 shmData.setKey(getShmKey(iCounter: infoPtr->shmCounter));
77 if (!shmData.create(size: infoPtr->shmDataSize)) {
78 close();
79 parent->setErrorString(QCoreApplication::translate(context: "", key: "Cannot create memory segment for file %1").arg(a: filename));
80 return false;
81 }
82 shmData.lock();
83 qint64 size = 0;
84 qint64 bytesRead;
85 char *data = static_cast<char *>(shmData.data());
86 bytesRead = f.read(data, maxlen: infoPtr->shmDataSize);
87 if (bytesRead != infoPtr->shmDataSize) {
88 close();
89 parent->setErrorString(QCoreApplication::translate(context: "", key: "Could not read data from %1 into shm").arg(a: filename));
90 return false;
91 }
92 shmDataSize = size;
93 shmData.unlock();
94 return true;
95}
96
97void KMemFile::Private::close()
98{
99 shmData.unlock();
100 shmData.detach();
101 shmInfo.unlock();
102 shmInfo.detach();
103 readWritePos = 0;
104 shmDataSize = 0;
105}
106
107KMemFile::KMemFile(const QString &filename, QObject *parent)
108 : QIODevice(parent)
109 , d(new Private(this))
110{
111 d->filename = filename;
112}
113
114KMemFile::~KMemFile()
115{
116 close();
117}
118
119void KMemFile::close()
120{
121 QIODevice::close();
122 if (!isOpen()) {
123 return;
124 }
125 d->close();
126}
127
128bool KMemFile::isSequential() const
129{
130 return false;
131}
132
133bool KMemFile::open(OpenMode mode)
134{
135 if (isOpen()) {
136 QIODevice::open(mode);
137 return false;
138 }
139
140 if (mode != QIODevice::ReadOnly) {
141 setErrorString(QCoreApplication::translate(context: "", key: "Only 'ReadOnly' allowed"));
142 return false;
143 }
144
145 if (!QFile::exists(fileName: d->filename)) {
146 setErrorString(QCoreApplication::translate(context: "", key: "File %1 does not exist").arg(a: d->filename));
147 return false;
148 }
149
150 QSharedMemory lock(QDir(d->filename).canonicalPath());
151 lock.lock();
152
153 Private::sharedInfoData *infoPtr;
154 d->shmInfo.setKey(d->getShmKey());
155 // see if it's already in memory
156 if (!d->shmInfo.attach(mode: QSharedMemory::ReadWrite)) {
157 if (!d->shmInfo.create(size: sizeof(Private::sharedInfoData))) {
158 lock.unlock();
159 setErrorString(QCoreApplication::translate(context: "", key: "Cannot create memory segment for file %1").arg(a: d->filename));
160 return false;
161 }
162 d->shmInfo.lock();
163 // no -> create it
164 infoPtr = static_cast<Private::sharedInfoData *>(d->shmInfo.data());
165 new (infoPtr) Private::sharedInfoData;
166 infoPtr->shmCounter = 1;
167 if (!d->loadContentsFromFile()) {
168 d->shmInfo.unlock();
169 d->shmInfo.detach();
170 lock.unlock();
171 return false;
172 }
173 } else {
174 d->shmInfo.lock();
175 infoPtr = static_cast<Private::sharedInfoData *>(d->shmInfo.data());
176 d->shmData.setKey(d->getShmKey(iCounter: infoPtr->shmCounter));
177 if (!d->shmData.attach(mode: QSharedMemory::ReadOnly)) {
178 if (!d->loadContentsFromFile()) {
179 d->shmInfo.unlock();
180 d->shmInfo.detach();
181 lock.unlock();
182 return false;
183 }
184 }
185 }
186 d->shmDataSize = infoPtr->shmDataSize;
187 d->shmInfo.unlock();
188 lock.unlock();
189
190 setOpenMode(mode);
191 return true;
192}
193
194bool KMemFile::seek(qint64 pos)
195{
196 if (d->shmDataSize < pos) {
197 setErrorString(QCoreApplication::translate(context: "", key: "Cannot seek past eof"));
198 return false;
199 }
200 d->readWritePos = pos;
201 QIODevice::seek(pos);
202 return true;
203}
204
205qint64 KMemFile::size() const
206{
207 return d->shmDataSize;
208}
209
210qint64 KMemFile::readData(char *data, qint64 maxSize)
211{
212 if ((openMode() & QIODevice::ReadOnly) == 0) {
213 return -1;
214 }
215
216 qint64 maxRead = size() - d->readWritePos;
217 qint64 bytesToRead = qMin(a: maxRead, b: maxSize);
218 const char *src = static_cast<const char *>(d->shmData.data());
219 memcpy(dest: data, src: &src[d->readWritePos], n: bytesToRead);
220 d->readWritePos += bytesToRead;
221 return bytesToRead;
222}
223
224qint64 KMemFile::writeData(const char *, qint64)
225{
226 return -1;
227}
228
229void KMemFile::fileContentsChanged(const QString &filename)
230{
231 QSharedMemory lock(QDir(filename).canonicalPath());
232 lock.lock();
233
234 QSharedMemory shmData(Private::getShmKey(filename));
235 if (!shmData.attach()) {
236 return;
237 }
238 shmData.lock();
239 Private::sharedInfoData *infoPtr = static_cast<Private::sharedInfoData *>(shmData.data());
240 infoPtr->shmCounter++;
241 infoPtr->shmDataSize = 0;
242 shmData.unlock();
243}
244
245#endif // QT_NO_SHAREDMEMORY
246
247#include "moc_kmemfile_p.cpp"
248

source code of kservice/src/sycoca/kmemfile.cpp