IPC: Move the legacy key to the QNativeIpcKey
This is needed to support passing it to other processes so they can enable legacy, compatibility mode. Right now, there's no such code, but I am 90% certain we'll need it soon in 6.6.x, if not for compatibility changes in the future. There's a bug in passing a QNativeIpcKey to another process that causes QSharedMemory to use the wrong QSystemSemaphore for control (a feature that should never have existed in the first place, but we're 15 years too late on that). I have not yet investigated a fix for this, but it will likely involve knowing the original legacy key. Pick-to: 6.6 6.6.0 Change-Id: Idd5e1bb52be047d7b4fffffd1750b547013cb336 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
parent
18867845eb
commit
9edb835904
@ -140,7 +140,6 @@ QSharedMemory::QSharedMemory(const QNativeIpcKey &key, QObject *parent)
|
||||
QSharedMemory::QSharedMemory(const QString &key, QObject *parent)
|
||||
: QSharedMemory(legacyNativeKey(key), parent)
|
||||
{
|
||||
d_func()->legacyKey = key;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -185,9 +184,7 @@ QSharedMemory::~QSharedMemory()
|
||||
*/
|
||||
void QSharedMemory::setKey(const QString &key)
|
||||
{
|
||||
Q_D(QSharedMemory);
|
||||
setNativeKey(legacyNativeKey(key));
|
||||
d->legacyKey = key;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -245,7 +242,6 @@ void QSharedMemory::setNativeKey(const QNativeIpcKey &key)
|
||||
if (isAttached())
|
||||
detach();
|
||||
d->cleanHandle();
|
||||
d->legacyKey = QString();
|
||||
if (key.type() == d->nativeKey.type()) {
|
||||
// we can reuse the backend
|
||||
d->nativeKey = key;
|
||||
@ -314,7 +310,7 @@ bool QSharedMemoryPrivate::initKey(SemaphoreAccessMode mode)
|
||||
QString QSharedMemory::key() const
|
||||
{
|
||||
Q_D(const QSharedMemory);
|
||||
return d->legacyKey;
|
||||
return QNativeIpcKeyPrivate::legacyKey(d->nativeKey);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -205,8 +205,6 @@ public:
|
||||
}
|
||||
QNativeIpcKey semaphoreNativeKey() const;
|
||||
#endif // QT_CONFIG(systemsemaphore)
|
||||
|
||||
QString legacyKey; // deprecated
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -84,7 +84,6 @@ inline void QSystemSemaphorePrivate::destructBackend()
|
||||
QSystemSemaphore::QSystemSemaphore(const QString &key, int initialValue, AccessMode mode)
|
||||
: QSystemSemaphore(legacyNativeKey(key), initialValue, mode)
|
||||
{
|
||||
d->legacyKey = key;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -210,8 +209,6 @@ void QSystemSemaphore::setNativeKey(const QNativeIpcKey &key, int initialValue,
|
||||
}
|
||||
d->initialValue = initialValue;
|
||||
d->handle(mode);
|
||||
|
||||
d->legacyKey.clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -244,7 +241,6 @@ QNativeIpcKey QSystemSemaphore::nativeIpcKey() const
|
||||
void QSystemSemaphore::setKey(const QString &key, int initialValue, AccessMode mode)
|
||||
{
|
||||
setNativeKey(legacyNativeKey(key), initialValue, mode);
|
||||
d->legacyKey = key;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -256,7 +252,7 @@ void QSystemSemaphore::setKey(const QString &key, int initialValue, AccessMode m
|
||||
*/
|
||||
QString QSystemSemaphore::key() const
|
||||
{
|
||||
return d->legacyKey;
|
||||
return QNativeIpcKeyPrivate::legacyKey(d->nativeKey);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -142,8 +142,6 @@ public:
|
||||
{
|
||||
return visit([&](auto p) { return p->modifySemaphore(this, count); });
|
||||
}
|
||||
|
||||
QString legacyKey; // deprecated
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <qstandardpaths.h>
|
||||
#include <qstringconverter.h>
|
||||
#include <qurl.h>
|
||||
#include <qurlquery.h>
|
||||
|
||||
#if defined(Q_OS_DARWIN)
|
||||
# include "private/qcore_mac_p.h"
|
||||
@ -93,10 +94,11 @@ static QNativeIpcKey::Type stringToType(QStringView typeString)
|
||||
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
|
||||
Returns a QNativeIpcKey that contains a platform-safe key using rules
|
||||
similar to QtIpcCommon::platformSafeKey() below, but using an algorithm
|
||||
that is compatible with Qt 4.4 to 6.6. Additionally, the returned
|
||||
QNativeIpcKey will record the input \a key so it can be included in the
|
||||
string form if necessary to pass to other processes.
|
||||
*/
|
||||
QNativeIpcKey QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType,
|
||||
QNativeIpcKey::Type type)
|
||||
@ -114,13 +116,14 @@ QNativeIpcKey QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon
|
||||
// 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.
|
||||
k.setNativeKey(key);
|
||||
QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, key, 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.
|
||||
k.setNativeKey(u'/' + QLatin1StringView(hex).left(SHM_NAME_MAX - 1));
|
||||
QString native = u'/' + QLatin1StringView(hex).left(SHM_NAME_MAX - 1);
|
||||
QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, native, key);
|
||||
}
|
||||
return k;
|
||||
#endif
|
||||
@ -147,20 +150,30 @@ QNativeIpcKey QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon
|
||||
switch (type) {
|
||||
case QNativeIpcKey::Type::Windows:
|
||||
if (isIpcSupported(ipcType, QNativeIpcKey::Type::Windows))
|
||||
k.setNativeKey(result);
|
||||
QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, result, key);
|
||||
return k;
|
||||
case QNativeIpcKey::Type::PosixRealtime:
|
||||
result.prepend(u'/');
|
||||
if (isIpcSupported(ipcType, QNativeIpcKey::Type::PosixRealtime))
|
||||
k.setNativeKey(result.prepend(u'/'));
|
||||
QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, result, key);
|
||||
return k;
|
||||
case QNativeIpcKey::Type::SystemV:
|
||||
break;
|
||||
}
|
||||
if (isIpcSupported(ipcType, QNativeIpcKey::Type::SystemV))
|
||||
k.setNativeKey(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + u'/' + result);
|
||||
if (isIpcSupported(ipcType, QNativeIpcKey::Type::SystemV)) {
|
||||
result = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + u'/' + result;
|
||||
QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, result, key);
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Returns a QNativeIpcKey of type \a type, suitable for QSystemSemaphore or
|
||||
QSharedMemory depending on \a ipcType. The returned native key is generated
|
||||
from the Unicode input \a key and is safe for use on for the key type in
|
||||
question in the current OS.
|
||||
*/
|
||||
QNativeIpcKey QtIpcCommon::platformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType,
|
||||
QNativeIpcKey::Type type)
|
||||
{
|
||||
@ -480,6 +493,7 @@ void QNativeIpcKey::setType_internal(Type type)
|
||||
*/
|
||||
void QNativeIpcKey::setNativeKey_internal(const QString &)
|
||||
{
|
||||
d->legacyKey_.clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -495,6 +509,8 @@ void QNativeIpcKey::setNativeKey_internal(const QString &)
|
||||
*/
|
||||
size_t qHash(const QNativeIpcKey &ipcKey, size_t seed) noexcept
|
||||
{
|
||||
// by *choice*, we're not including d->legacyKey_ in the hash -- it's
|
||||
// already partially encoded in the key
|
||||
return qHashMulti(seed, ipcKey.key, ipcKey.type());
|
||||
}
|
||||
|
||||
@ -506,8 +522,7 @@ size_t qHash(const QNativeIpcKey &ipcKey, size_t seed) noexcept
|
||||
*/
|
||||
int QNativeIpcKey::compare_internal(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
|
||||
{
|
||||
Q_UNUSED(lhs); Q_UNUSED(rhs);
|
||||
return 0;
|
||||
return (QNativeIpcKeyPrivate::legacyKey(lhs) == QNativeIpcKeyPrivate::legacyKey(rhs)) ? 0 : 1;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -536,6 +551,12 @@ QString QNativeIpcKey::toString() const
|
||||
QUrl u;
|
||||
u.setScheme(prefix);
|
||||
u.setPath(copy, QUrl::TolerantMode);
|
||||
if (isSlowPath()) {
|
||||
QUrlQuery q;
|
||||
if (!d->legacyKey_.isEmpty())
|
||||
q.addQueryItem(u"legacyKey"_s, QString(d->legacyKey_).replace(u'%', "%25"_L1));
|
||||
u.setQuery(q);
|
||||
}
|
||||
return u.toString(QUrl::DecodeReserved);
|
||||
}
|
||||
|
||||
@ -555,7 +576,7 @@ QNativeIpcKey QNativeIpcKey::fromString(const QString &text)
|
||||
Type invalidType = {};
|
||||
Type type = stringToType(u.scheme());
|
||||
if (type == invalidType || !u.isValid() || !u.userInfo().isEmpty() || !u.host().isEmpty()
|
||||
|| u.port() != -1 || u.hasQuery())
|
||||
|| u.port() != -1)
|
||||
return QNativeIpcKey(invalidType);
|
||||
|
||||
QNativeIpcKey result(QString(), type);
|
||||
@ -565,6 +586,18 @@ QNativeIpcKey QNativeIpcKey::fromString(const QString &text)
|
||||
// decode the payload
|
||||
result.setNativeKey(u.path());
|
||||
|
||||
if (u.hasQuery()) {
|
||||
const QList items = QUrlQuery(u).queryItems();
|
||||
for (const auto &item : items) {
|
||||
if (item.first == u"legacyKey"_s) {
|
||||
QString legacyKey = QUrl::fromPercentEncoding(item.second.toUtf8());
|
||||
QNativeIpcKeyPrivate::setLegacyKey(result, std::move(legacyKey));
|
||||
} else {
|
||||
// unknown query item
|
||||
return QNativeIpcKey(invalidType);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,32 @@ QT_BEGIN_NAMESPACE
|
||||
class QNativeIpcKeyPrivate
|
||||
{
|
||||
public:
|
||||
QString legacyKey_;
|
||||
|
||||
static QString legacyKey(const QNativeIpcKey &key)
|
||||
{
|
||||
if (key.isSlowPath())
|
||||
return key.d->legacyKey_;
|
||||
return QString();
|
||||
}
|
||||
static void setLegacyKey(QNativeIpcKey &key, const QString &legacyKey)
|
||||
{
|
||||
QNativeIpcKeyPrivate::makeExtended(key)->legacyKey_ = legacyKey;
|
||||
}
|
||||
static void setNativeAndLegacyKeys(QNativeIpcKey &key, const QString &nativeKey,
|
||||
const QString &legacyKey)
|
||||
{
|
||||
key.setNativeKey(nativeKey);
|
||||
setLegacyKey(key, legacyKey);
|
||||
}
|
||||
|
||||
private:
|
||||
static QNativeIpcKeyPrivate *makeExtended(QNativeIpcKey &key)
|
||||
{
|
||||
if (!key.isSlowPath())
|
||||
key.d = new QNativeIpcKeyPrivate;
|
||||
return key.d;
|
||||
}
|
||||
};
|
||||
|
||||
namespace QtIpcCommon {
|
||||
|
@ -6,6 +6,19 @@
|
||||
|
||||
#include "../ipctestcommon.h"
|
||||
|
||||
#if QT_CONFIG(sharedmemory)
|
||||
# include <qsharedmemory.h>
|
||||
#endif
|
||||
#if QT_CONFIG(systemsemaphore)
|
||||
# include <qsystemsemaphore.h>
|
||||
#endif
|
||||
|
||||
#if QT_CONFIG(sharedmemory)
|
||||
static const auto makeLegacyKey = QSharedMemory::legacyNativeKey;
|
||||
#else
|
||||
static const auto makeLegacyKey = QSystemSemaphore::legacyNativeKey;
|
||||
#endif
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
class tst_QNativeIpcKey : public QObject
|
||||
@ -22,6 +35,8 @@ private slots:
|
||||
void toString();
|
||||
void fromString_data();
|
||||
void fromString();
|
||||
void legacyKeys_data();
|
||||
void legacyKeys();
|
||||
};
|
||||
|
||||
void tst_QNativeIpcKey::defaultTypes()
|
||||
@ -181,6 +196,19 @@ void tst_QNativeIpcKey::equality()
|
||||
key2.setType(QNativeIpcKey::DefaultTypeForOs);
|
||||
QCOMPARE(key1, key2);
|
||||
QVERIFY(!(key1 != key2));
|
||||
|
||||
key1 = makeLegacyKey("key1", QNativeIpcKey::DefaultTypeForOs);
|
||||
QCOMPARE_NE(key1, key2);
|
||||
QVERIFY(!(key1 == key2));
|
||||
|
||||
key2 = key1;
|
||||
QCOMPARE(key1, key2);
|
||||
QVERIFY(!(key1 != key2));
|
||||
|
||||
// just setting the native key won't make them equal again!
|
||||
key2.setNativeKey(key1.nativeKey());
|
||||
QCOMPARE_NE(key1, key2);
|
||||
QVERIFY(!(key1 == key2));
|
||||
}
|
||||
|
||||
void tst_QNativeIpcKey::hash()
|
||||
@ -215,6 +243,12 @@ void tst_QNativeIpcKey::swap()
|
||||
QCOMPARE(key1.type(), QNativeIpcKey::Type::PosixRealtime);
|
||||
QCOMPARE(key2.nativeKey(), "key2");
|
||||
QCOMPARE(key2.type(), QNativeIpcKey::Type::Windows);
|
||||
|
||||
key1 = makeLegacyKey("key1", QNativeIpcKey::DefaultTypeForOs);
|
||||
QCOMPARE(key1.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||
key1.swap(key2);
|
||||
QCOMPARE(key1.type(), QNativeIpcKey::Type::Windows);
|
||||
QCOMPARE(key2.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||
}
|
||||
|
||||
void tst_QNativeIpcKey::toString_data()
|
||||
@ -323,5 +357,70 @@ void tst_QNativeIpcKey::fromString()
|
||||
QCOMPARE(QNativeIpcKey::fromString(string), key);
|
||||
}
|
||||
|
||||
void tst_QNativeIpcKey::legacyKeys_data()
|
||||
{
|
||||
QTest::addColumn<QNativeIpcKey::Type>("type");
|
||||
QTest::addColumn<QString>("legacyKey");
|
||||
auto addRows = [](QNativeIpcKey::Type type) {
|
||||
const char *label = "<unknown-type>";
|
||||
switch (type) {
|
||||
case QNativeIpcKey::Type::SystemV:
|
||||
label = "systemv";
|
||||
break;
|
||||
case QNativeIpcKey::Type::PosixRealtime:
|
||||
label = "posix";
|
||||
break;
|
||||
case QNativeIpcKey::Type::Windows:
|
||||
label = "windows";
|
||||
break;
|
||||
}
|
||||
auto add = [=](const char *name, const QString &legacyKey) {
|
||||
QTest::addRow("%s-%s", label, name) << type << legacyKey;
|
||||
};
|
||||
add("empty", {});
|
||||
add("text", "foobar"_L1);
|
||||
add("pathlike", "/sometext"_L1);
|
||||
add("objectlike", "Global\\sometext"_L1);
|
||||
add("colon-slash", ":/"_L1);
|
||||
add("slash-colon", "/:"_L1);
|
||||
add("percent", "%"_L1);
|
||||
add("question-hash", "?#"_L1);
|
||||
add("hash-question", "#?"_L1);
|
||||
add("double-slash", "//"_L1);
|
||||
add("triple-slash", "///"_L1);
|
||||
add("non-ascii", "\xe9"_L1);
|
||||
add("non-utf8", "\xa0\xff"_L1);
|
||||
add("non-latin1", u":\u0100.\u2000.\U00010000"_s);
|
||||
};
|
||||
|
||||
addRows(QNativeIpcKey::DefaultTypeForOs);
|
||||
if (auto type = QNativeIpcKey::legacyDefaultTypeForOs();
|
||||
type != QNativeIpcKey::DefaultTypeForOs)
|
||||
addRows(type);
|
||||
}
|
||||
|
||||
void tst_QNativeIpcKey::legacyKeys()
|
||||
{
|
||||
QFETCH(QNativeIpcKey::Type, type);
|
||||
QFETCH(QString, legacyKey);
|
||||
|
||||
QNativeIpcKey key = makeLegacyKey(legacyKey, type);
|
||||
QCOMPARE(key.type(), type);
|
||||
|
||||
QString string = key.toString();
|
||||
QNativeIpcKey key2 = QNativeIpcKey::fromString(string);
|
||||
QCOMPARE(key2, key);
|
||||
|
||||
if (!legacyKey.isEmpty()) {
|
||||
// confirm it shows up in the encoded form
|
||||
Q_ASSERT(!legacyKey.contains(u'&')); // needs extra encoding
|
||||
QUrl u;
|
||||
u.setQuery("legacyKey="_L1 + legacyKey, QUrl::DecodedMode);
|
||||
QString encodedLegacyKey = u.toString(QUrl::RemoveScheme | QUrl::RemoveAuthority
|
||||
| QUrl::DecodeReserved);
|
||||
QVERIFY2(string.contains(encodedLegacyKey), qPrintable(string));
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QNativeIpcKey)
|
||||
#include "tst_qnativeipckey.moc"
|
||||
|
Loading…
Reference in New Issue
Block a user