1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "sharedmemory.h"
27 #include <qdir.h>
28 #include <qcryptographichash.h>
29 
30 #include "qplatformdefs.h"
31 
32 #include <errno.h>
33 
34 #include <sys/mman.h>
35 #include <sys/stat.h> /* For mode constants */
36 #include <fcntl.h> /* For O_* constants */
37 
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #ifdef Q_OS_OSX
41 #include <sys/posix_shm.h>
42 #endif
43 #include <fcntl.h>
44 #include <unistd.h>
45 
46 #include <private/qcore_unix_p.h>
47 
48 namespace QmlDesigner {
49 
50 class SharedMemoryLocker
51 {
52 public:
SharedMemoryLocker(SharedMemory * sharedMemory)53     SharedMemoryLocker(SharedMemory *sharedMemory) : m_sharedMemory(sharedMemory)
54     {
55         Q_ASSERT(m_sharedMemory);
56     }
57 
~SharedMemoryLocker()58     ~SharedMemoryLocker()
59     {
60         if (m_sharedMemory)
61             m_sharedMemory->unlock();
62     }
63 
tryLocker(const QString & function)64     bool tryLocker(const QString &function) {
65         if (!m_sharedMemory)
66             return false;
67 
68         if (m_sharedMemory->lock())
69             return true;
70 
71         m_sharedMemory->m_errorString = QStringLiteral("%1: unable to lock").arg(function);
72         m_sharedMemory->m_error = QSharedMemory::LockError;
73         m_sharedMemory = nullptr;
74         return false;
75     }
76 
77 private:
78     SharedMemory *m_sharedMemory;
79 };
80 
makePlatformSafeKey(const QString & key)81 static QByteArray makePlatformSafeKey(const QString &key)
82 {
83     if (key.isEmpty())
84         return QByteArray();
85     QByteArray data(QCryptographicHash::hash(key.toLatin1(), QCryptographicHash::Sha1).toBase64());
86 
87     data = data.replace('+', '-');
88     data = data.replace('/', '_');
89 
90     data.truncate(31); // OS X is only supporting 31 byte long names
91 
92     return data;
93 }
94 
95 
SharedMemory()96 SharedMemory::SharedMemory()
97     : m_memory(nullptr),
98       m_size(0),
99       m_error(QSharedMemory::NoError),
100       m_systemSemaphore(QString()),
101       m_lockedByMe(false),
102       m_fileHandle(-1),
103       m_createdByMe(false)
104 {
105 }
106 
SharedMemory(const QString & key)107 SharedMemory::SharedMemory(const QString &key)
108     : m_memory(nullptr),
109       m_size(0),
110       m_error(QSharedMemory::NoError),
111       m_systemSemaphore(QString()),
112       m_lockedByMe(false),
113       m_fileHandle(-1),
114       m_createdByMe(false)
115 {
116     setKey(key);
117 }
118 
~SharedMemory()119 SharedMemory::~SharedMemory()
120 {
121     if (m_memory) {
122         munmap(m_memory, m_size);
123         m_memory = nullptr;
124         m_size = 0;
125     }
126 
127     if (m_fileHandle != -1) {
128         close(m_fileHandle);
129         cleanHandleInternal();
130         if (m_createdByMe)
131             shm_unlink(m_nativeKey);
132     }
133 
134     setKey(QString());
135 }
136 
setKey(const QString & key)137 void SharedMemory::setKey(const QString &key)
138 {
139     if (key == m_key && makePlatformSafeKey(key) == m_nativeKey)
140         return;
141 
142     if (isAttached())
143         detach();
144 
145     m_key = key;
146     m_nativeKey = makePlatformSafeKey(key);
147 }
148 
key() const149 QString SharedMemory::key() const
150 {
151     return m_key;
152 }
153 
create(int size,QSharedMemory::AccessMode mode)154 bool SharedMemory::create(int size, QSharedMemory::AccessMode mode)
155 {
156     if (!initKeyInternal())
157         return false;
158 
159 
160     m_systemSemaphore.setKey(m_key, 1, QSystemSemaphore::Create);
161 
162 
163     QString function = QLatin1String("SharedMemory::create");
164 
165     SharedMemoryLocker lock(this);
166     if (!m_key.isNull() && !lock.tryLocker(function))
167         return false;
168 
169     if (size <= 0) {
170         m_error = QSharedMemory::InvalidSize;
171         m_errorString = QStringLiteral("%1: create size is less then 0").arg(function);
172         return false;
173     }
174 
175     return createInternal(mode, size_t(size));
176 }
177 
size() const178 int SharedMemory::size() const
179 {
180     return int(m_size);
181 }
182 
attach(QSharedMemory::AccessMode mode)183 bool SharedMemory::attach(QSharedMemory::AccessMode mode)
184 {
185     if (isAttached() || !initKeyInternal())
186         return false;
187 
188     SharedMemoryLocker lock(this);
189     if (!m_key.isNull() && !lock.tryLocker(QStringLiteral("SharedMemory::attach")))
190         return false;
191 
192     if (isAttached() || !handle())
193         return false;
194 
195     return attachInternal(mode);
196 }
197 
198 
isAttached() const199 bool SharedMemory::isAttached() const
200 {
201     return m_memory != nullptr;
202 }
203 
detach()204 bool SharedMemory::detach()
205 {
206     if (!isAttached())
207         return false;
208 
209     SharedMemoryLocker lock(this);
210     if (!m_key.isNull() && !lock.tryLocker(QStringLiteral("SharedMemory::detach")))
211         return false;
212 
213     return detachInternal();
214 }
215 
data()216 void *SharedMemory::data()
217 {
218     return m_memory;
219 }
220 
constData() const221 const void* SharedMemory::constData() const
222 {
223     return m_memory;
224 }
225 
data() const226 const void *SharedMemory::data() const
227 {
228     return m_memory;
229 }
230 
lock()231 bool SharedMemory::lock()
232 {
233     if (m_lockedByMe) {
234         qWarning("SharedMemory::lock: already locked");
235         return true;
236     }
237     if (m_systemSemaphore.acquire()) {
238         m_lockedByMe = true;
239         return true;
240     }
241     QString function = QStringLiteral("SharedMemory::lock");
242     m_errorString = QStringLiteral("%1: unable to lock").arg(function);
243     m_error = QSharedMemory::LockError;
244     return false;
245 }
246 
unlock()247 bool SharedMemory::unlock()
248 {
249     if (!m_lockedByMe)
250         return false;
251     m_lockedByMe = false;
252     if (m_systemSemaphore.release())
253         return true;
254     QString function = QStringLiteral("SharedMemory::unlock");
255     m_errorString = QStringLiteral("%1: unable to unlock").arg(function);
256     m_error = QSharedMemory::LockError;
257     return false;
258 }
259 
error() const260 QSharedMemory::SharedMemoryError SharedMemory::error() const
261 {
262     return m_error;
263 }
264 
errorString() const265 QString SharedMemory::errorString() const
266 {
267     return m_errorString;
268 }
269 
setErrorString(const QString & function)270 void SharedMemory::setErrorString(const QString &function)
271 {
272     // EINVAL is handled in functions so they can give better error strings
273     switch (errno) {
274     case EACCES:
275         m_errorString = QStringLiteral("%1: permission denied").arg(function);
276         m_error = QSharedMemory::PermissionDenied;
277         break;
278     case EEXIST:
279         m_errorString = QStringLiteral("%1: already exists").arg(function);
280         m_error = QSharedMemory::AlreadyExists;
281         break;
282     case ENOENT:
283         m_errorString = QStringLiteral("%1: doesn't exist").arg(function);
284         m_error = QSharedMemory::NotFound;
285         break;
286     case EMFILE:
287     case ENOMEM:
288     case ENOSPC:
289         m_errorString = QStringLiteral("%1: out of resources").arg(function);
290         m_error = QSharedMemory::OutOfResources;
291         break;
292     default:
293         m_errorString = QStringLiteral("%1: unknown error %2")
294                 .arg(function).arg(QString::fromLocal8Bit(strerror(errno)));
295         m_error = QSharedMemory::UnknownError;
296     }
297 }
298 
initKeyInternal()299 bool SharedMemory::initKeyInternal()
300 {
301     cleanHandleInternal();
302 
303     m_systemSemaphore.setKey(QString(), 1);
304     m_systemSemaphore.setKey(m_key, 1);
305     if (m_systemSemaphore.error() != QSystemSemaphore::NoError) {
306         m_errorString = QStringLiteral("SharedMemoryPrivate::initKey: unable to set key on lock");
307         switch (m_systemSemaphore.error()) {
308         case QSystemSemaphore::PermissionDenied:
309             m_error = QSharedMemory::PermissionDenied;
310             break;
311         case QSystemSemaphore::KeyError:
312             m_error = QSharedMemory::KeyError;
313             break;
314         case QSystemSemaphore::AlreadyExists:
315             m_error = QSharedMemory::AlreadyExists;
316             break;
317         case QSystemSemaphore::NotFound:
318             m_error = QSharedMemory::NotFound;
319             break;
320         case QSystemSemaphore::OutOfResources:
321             m_error = QSharedMemory::OutOfResources;
322             break;
323         case QSystemSemaphore::UnknownError:
324         default:
325             m_error = QSharedMemory::UnknownError;
326             break;
327         }
328 
329         return false;
330     }
331 
332     m_errorString.clear();
333     m_error = QSharedMemory::NoError;
334     return true;
335 }
336 
handle()337 int SharedMemory::handle()
338 {
339     return m_fileHandle;
340 }
341 
cleanHandleInternal()342 void SharedMemory::cleanHandleInternal()
343 {
344     m_fileHandle = -1;
345 }
346 
createInternal(QSharedMemory::AccessMode mode,size_t size)347 bool SharedMemory::createInternal(QSharedMemory::AccessMode mode, size_t size)
348 {
349     detachInternal();
350 
351 #ifdef Q_OS_OSX
352     if (m_fileHandle > -1 && m_createdByMe) {
353         close(m_fileHandle);
354         shm_unlink(m_nativeKey.constData());
355         m_fileHandle = -1;
356     }
357 #endif
358 
359     if (m_fileHandle == -1) {
360         int permission = mode == QSharedMemory::ReadOnly ? O_RDONLY : O_RDWR;
361         m_fileHandle = shm_open(m_nativeKey.constData(), O_CREAT | permission, 0666);
362 
363         if (m_fileHandle == -1) {
364             switch (errno) {
365             case EINVAL:
366                 m_errorString = QStringLiteral("QSharedMemory::create: key is not invalid");
367                 m_error = QSharedMemory::KeyError;
368                 break;
369             case EMFILE:
370                 m_errorString = QStringLiteral("QSharedMemory::create: maximum file limit reached");
371                 m_error = QSharedMemory::UnknownError;
372                 break;
373             case ENAMETOOLONG:
374                 m_errorString = QStringLiteral("QSharedMemory::create: key is to long");
375                 m_error = QSharedMemory::KeyError;
376                 break;
377             default:
378                 setErrorString(QStringLiteral("SharedMemory::create"));
379             }
380             return false;
381         }
382 
383         m_createdByMe = true;
384     }
385 
386     struct stat statBuffer;
387     if (fstat(m_fileHandle, &statBuffer) == -1)
388         return false;
389     size_t fileSize = size_t(statBuffer.st_size);
390 
391     if (fileSize < size) {
392         int returnValue = ftruncate(m_fileHandle, ssize_t(size));
393         if (returnValue == -1) {
394             switch (errno) {
395             case EFBIG:
396                 m_errorString = QStringLiteral("QSharedMemory::create: size is to large");
397                 m_error = QSharedMemory::InvalidSize;
398                 break;
399             default:
400                 setErrorString(QStringLiteral("SharedMemory::create"));
401             }
402 
403             close(m_fileHandle);
404             shm_unlink(m_nativeKey.constData());
405             m_fileHandle = -1;
406             m_size = 0;
407 
408             return false;
409         }
410     }
411 
412     int protection = mode == QSharedMemory::ReadOnly ? PROT_READ : PROT_WRITE;
413     m_memory = mmap(nullptr, size, protection, MAP_SHARED, m_fileHandle, 0);
414 
415     if (m_memory == MAP_FAILED) {
416         close(m_fileHandle);
417         shm_unlink(m_nativeKey.constData());
418         m_memory = nullptr;
419         m_fileHandle = -1;
420         m_size = 0;
421 
422         return false;
423     }
424 
425     m_size = size;
426 
427     return true;
428 }
429 
attachInternal(QSharedMemory::AccessMode mode)430 bool SharedMemory::attachInternal(QSharedMemory::AccessMode mode)
431 {
432     if (m_fileHandle == -1) {
433         int permission = mode == QSharedMemory::ReadOnly ? O_RDONLY : O_RDWR;
434         m_fileHandle = shm_open(m_nativeKey.constData(), permission, 0666);
435         if (m_fileHandle == -1) {
436             switch (errno) {
437             case EINVAL:
438                 m_errorString = QStringLiteral("QSharedMemory::attach: key is invalid");
439                 m_error = QSharedMemory::KeyError;
440                 break;
441             case EMFILE:
442                 m_errorString = QStringLiteral("QSharedMemory::attach: maximum file limit reached");
443                 m_error = QSharedMemory::UnknownError;
444                 break;
445             case ENAMETOOLONG:
446                 m_errorString = QStringLiteral("QSharedMemory::attach: key is to long");
447                 m_error = QSharedMemory::KeyError;
448                 break;
449             default:
450                 setErrorString(QStringLiteral("SharedMemory::attach"));
451             }
452             return false;
453         }
454     }
455 
456     struct stat statBuffer;
457     if (fstat(m_fileHandle, &statBuffer) == -1)
458         return false;
459     size_t size = size_t(statBuffer.st_size);
460 
461     int protection = mode == QSharedMemory::ReadOnly ? PROT_READ : PROT_WRITE;
462     m_memory = mmap(nullptr, size, protection, MAP_SHARED, m_fileHandle, 0);
463 
464     if (m_memory == MAP_FAILED) {
465         m_memory = nullptr;
466 
467         return false;
468     }
469 
470     m_size = size;
471 
472     return true;
473 }
474 
detachInternal()475 bool SharedMemory::detachInternal()
476 {
477     if (m_memory) {
478         munmap(m_memory, m_size);
479         m_memory = nullptr;
480         m_size = 0;
481     }
482 
483     return false;
484 }
485 
486 } // namespace QmlDesigner
487