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

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