IPC: move makePlatformSafeKey to qtipccommon.cpp

This removes the second portion of the #if mess of shared code between
QSharedMemory and QSystemSemaphore for SystemV.

Change-Id: Id8d5e3999fe94b03acc1fffd171c073c2873206e
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Thiago Macieira 2022-10-08 00:25:17 -07:00
parent 461463b75d
commit 7809caa924
8 changed files with 114 additions and 94 deletions

View File

@ -110,6 +110,7 @@ qt_internal_add_module(Core
global/qxptype_traits.h
ipc/qsharedmemory.cpp ipc/qsharedmemory.h ipc/qsharedmemory_p.h
ipc/qsystemsemaphore.cpp ipc/qsystemsemaphore.h ipc/qsystemsemaphore_p.h
ipc/qtipccommon.cpp ipc/qtipccommon_p.h
io/qabstractfileengine.cpp io/qabstractfileengine_p.h
io/qbuffer.cpp io/qbuffer.h
io/qdataurl.cpp io/qdataurl_p.h

View File

@ -3,81 +3,19 @@
#include "qsharedmemory.h"
#include "qsharedmemory_p.h"
#include "qtipccommon_p.h"
#include "qsystemsemaphore.h"
#include <qdir.h>
#include <qcryptographichash.h>
#include <qdebug.h>
#ifdef Q_OS_WIN
# include <qt_windows.h>
#endif
#if defined(Q_OS_DARWIN)
# include "qcore_mac_p.h"
# if !defined(SHM_NAME_MAX)
// Based on PSEMNAMLEN in XNU's posix_sem.c, which would
// indicate the max length is 31, _excluding_ the zero
// terminator. But in practice (possibly due to an off-
// by-one bug in the kernel) the usable bytes are only 30.
# define SHM_NAME_MAX 30
# endif
#endif
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
#if QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
/*!
\internal
Generate a string from the key which can be any unicode string into
the subset that the win/unix kernel allows.
On Unix this will be a file name
*/
QString
QSharedMemoryPrivate::makePlatformSafeKey(const QString &key,
const QString &prefix)
{
if (key.isEmpty())
return QString();
QByteArray hex = QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Sha1).toHex();
#if defined(Q_OS_DARWIN) && defined(QT_POSIX_IPC)
if (qt_apple_isSandboxed()) {
// Sandboxed applications on Apple platforms require the shared memory name
// to be in the form <application group identifier>/<custom identifier>.
// Since we don't know which application group identifier the user wants
// to apply, we instead document that requirement, and use the key directly.
return key;
} else {
// The shared memory name limit on Apple platforms is very low (30 characters),
// so we can't use the logic below of combining the prefix, key, and a hash,
// to ensure a unique and valid name. Instead we use the first part of the
// hash, which should still long enough to avoid collisions in practice.
return u'/' + hex.left(SHM_NAME_MAX - 1);
}
#endif
QString result = prefix;
for (QChar ch : key) {
if ((ch >= u'a' && ch <= u'z') ||
(ch >= u'A' && ch <= u'Z'))
result += ch;
}
result.append(QLatin1StringView(hex));
#ifdef Q_OS_WIN
return result;
#elif defined(QT_POSIX_IPC)
return u'/' + result;
#else
return QDir::tempPath() + u'/' + result;
#endif
}
#endif // QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
#if QT_CONFIG(sharedmemory)
/*!
@ -306,14 +244,16 @@ QSharedMemory::~QSharedMemory()
void QSharedMemory::setKey(const QString &key)
{
Q_D(QSharedMemory);
if (key == d->key && d->makePlatformSafeKey(key) == d->nativeKey)
QString newNativeKey =
QtIpcCommon::legacyPlatformSafeKey(key, QtIpcCommon::IpcType::SharedMemory);
if (key == d->key && newNativeKey == d->nativeKey)
return;
if (isAttached())
detach();
d->cleanHandle();
d->key = key;
d->nativeKey = d->makePlatformSafeKey(key);
d->nativeKey = newNativeKey;
}
/*!

View File

@ -19,22 +19,7 @@
#include <QtCore/qstring.h>
#if !QT_CONFIG(sharedmemory)
# if QT_CONFIG(systemsemaphore)
QT_BEGIN_NAMESPACE
namespace QSharedMemoryPrivate
{
QString makePlatformSafeKey(const QString &key,
const QString &prefix = QStringLiteral("qipc_sharedmemory_"));
}
QT_END_NAMESPACE
# endif
#else
#if QT_CONFIG(sharedmemory)
#include "qsystemsemaphore.h"
#include "private/qobject_p.h"
@ -92,8 +77,6 @@ public:
bool lockedByMe = false;
#endif
static QString makePlatformSafeKey(const QString &key,
const QString &prefix = QStringLiteral("qipc_sharedmemory_"));
#ifdef Q_OS_WIN
Qt::HANDLE handle();
#elif defined(QT_POSIX_IPC)

View File

@ -7,7 +7,7 @@
#include "qsharedmemory.h"
#include "qsharedmemory_p.h"
#include "qsystemsemaphore.h"
#include "qtipccommon_p.h"
#include <qfile.h>
#include <errno.h>
@ -26,11 +26,12 @@
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
using namespace QtIpcCommon;
int QSharedMemoryPrivate::handle()
{
// don't allow making handles on empty keys
const QString safeKey = makePlatformSafeKey(key);
const QString safeKey = legacyPlatformSafeKey(key, QtIpcCommon::IpcType::SharedMemory);
if (safeKey.isEmpty()) {
errorString = QSharedMemory::tr("%1: key is empty").arg("QSharedMemory::handle"_L1);
error = QSharedMemory::KeyError;
@ -53,7 +54,7 @@ bool QSharedMemoryPrivate::create(qsizetype size)
if (!handle())
return false;
const QByteArray shmName = QFile::encodeName(makePlatformSafeKey(key));
const QByteArray shmName = QFile::encodeName(legacyPlatformSafeKey(key, IpcType::SharedMemory));
int fd;
#ifdef O_CLOEXEC
@ -94,7 +95,7 @@ bool QSharedMemoryPrivate::create(qsizetype size)
bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode)
{
const QByteArray shmName = QFile::encodeName(makePlatformSafeKey(key));
const QByteArray shmName = QFile::encodeName(legacyPlatformSafeKey(key, IpcType::SharedMemory));
const int oflag = (mode == QSharedMemory::ReadOnly ? O_RDONLY : O_RDWR);
const mode_t omode = (mode == QSharedMemory::ReadOnly ? 0400 : 0600);
@ -180,7 +181,7 @@ bool QSharedMemoryPrivate::detach()
// if there are no attachments then unlink the shared memory
if (shm_nattch == 0) {
const QByteArray shmName = QFile::encodeName(makePlatformSafeKey(key));
const QByteArray shmName = QFile::encodeName(legacyPlatformSafeKey(key, IpcType::SharedMemory));
if (::shm_unlink(shmName.constData()) == -1 && errno != ENOENT)
setErrorString("QSharedMemory::detach (shm_unlink)"_L1);
}

View File

@ -21,7 +21,6 @@
#include "qcoreapplication.h"
#include "qtipccommon_p.h"
#include "qsharedmemory_p.h"
#include <sys/types.h>
#ifdef QT_POSIX_IPC
@ -37,7 +36,7 @@ public:
QString makeKeyFileName()
{
return QSharedMemoryPrivate::makePlatformSafeKey(key, QLatin1StringView("qipc_systemsem_"));
return QtIpcCommon::legacyPlatformSafeKey(key, QtIpcCommon::IpcType::SystemSemaphore);
}
inline void setError(QSystemSemaphore::SystemSemaphoreError e, const QString &message)

View File

@ -0,0 +1,89 @@
// Copyright (C) 2022 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qtipccommon_p.h"
#include <qcryptographichash.h>
#include <qdir.h>
#if defined(Q_OS_DARWIN)
# include "private/qcore_mac_p.h"
# if !defined(SHM_NAME_MAX)
// Based on PSEMNAMLEN in XNU's posix_sem.c, which would
// indicate the max length is 31, _excluding_ the zero
// terminator. But in practice (possibly due to an off-
// by-one bug in the kernel) the usable bytes are only 30.
# define SHM_NAME_MAX 30
# endif
#endif
#if QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
/*!
\internal
Legacy: this exists for compatibility with QSharedMemory and
QSystemSemaphore between 4.4 and 6.6.
Generate a string from the key which can be any unicode string into
the subset that the win/unix kernel allows.
On Unix this will be a file name
*/
QString QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType)
{
if (key.isEmpty())
return QString();
QByteArray hex = QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Sha1).toHex();
#if defined(Q_OS_DARWIN) && defined(QT_POSIX_IPC)
if (qt_apple_isSandboxed()) {
// Sandboxed applications on Apple platforms require the shared memory name
// to be in the form <application group identifier>/<custom identifier>.
// Since we don't know which application group identifier the user wants
// to apply, we instead document that requirement, and use the key directly.
return key;
} else {
// The shared memory name limit on Apple platforms is very low (30 characters),
// so we can't use the logic below of combining the prefix, key, and a hash,
// to ensure a unique and valid name. Instead we use the first part of the
// hash, which should still long enough to avoid collisions in practice.
return u'/' + hex.left(SHM_NAME_MAX - 1);
}
#endif
QString result;
result.reserve(1 + 18 + key.size() + 40);
switch (ipcType) {
case IpcType::SharedMemory:
result += "qipc_sharedmemory_"_L1;
break;
case IpcType::SystemSemaphore:
result += "qipc_systemsem_"_L1;
break;
}
for (QChar ch : key) {
if ((ch >= u'a' && ch <= u'z') ||
(ch >= u'A' && ch <= u'Z'))
result += ch;
}
result.append(QLatin1StringView(hex));
#ifdef Q_OS_WIN
return result;
#elif defined(QT_POSIX_IPC)
return u'/' + result;
#else
return QDir::tempPath() + u'/' + result;
#endif
}
QT_END_NAMESPACE
#endif // QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)

View File

@ -27,6 +27,13 @@
QT_BEGIN_NAMESPACE
namespace QtIpcCommon {
enum class IpcType {
SharedMemory,
SystemSemaphore
};
Q_AUTOTEST_EXPORT QString legacyPlatformSafeKey(const QString &key, IpcType ipcType);
#ifdef Q_OS_UNIX
// Convenience function to create the file if needed
inline int createUnixKeyFile(const QByteArray &fileName)

View File

@ -135,7 +135,7 @@ void tst_QSharedMemory::cleanup()
}
#ifndef Q_OS_WIN
#include <private/qsharedmemory_p.h>
#include <private/qtipccommon_p.h>
#include <sys/types.h>
#ifndef QT_POSIX_IPC
#include <sys/ipc.h>
@ -157,7 +157,7 @@ int tst_QSharedMemory::remove(const QString &key)
if (key.isEmpty())
return -1;
QString fileName = QSharedMemoryPrivate::makePlatformSafeKey(key);
QString fileName = QtIpcCommon::legacyPlatformSafeKey(key, QtIpcCommon::IpcType::SharedMemory);
#ifndef QT_POSIX_IPC
// ftok requires that an actual file exists somewhere