diff options
Diffstat (limited to 'src/corelib/ipc/qtipccommon.cpp')
-rw-r--r-- | src/corelib/ipc/qtipccommon.cpp | 246 |
1 files changed, 151 insertions, 95 deletions
diff --git a/src/corelib/ipc/qtipccommon.cpp b/src/corelib/ipc/qtipccommon.cpp index 46a4777104..b2ae9172fa 100644 --- a/src/corelib/ipc/qtipccommon.cpp +++ b/src/corelib/ipc/qtipccommon.cpp @@ -7,7 +7,8 @@ #include <qcryptographichash.h> #include <qstandardpaths.h> #include <qstringconverter.h> -#include <private/qurl_p.h> +#include <qurl.h> +#include <qurlquery.h> #if defined(Q_OS_DARWIN) # include "private/qcore_mac_p.h" @@ -18,6 +19,8 @@ // by-one bug in the kernel) the usable bytes are only 30. # define SHM_NAME_MAX 30 # endif +#elif defined(Q_OS_WINDOWS) +# include "qt_windows.h" #endif #if QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore) @@ -91,16 +94,18 @@ 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. */ -QString QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType, - QNativeIpcKey::Type type) +QNativeIpcKey QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType, + QNativeIpcKey::Type type) { + QNativeIpcKey k(type); if (key.isEmpty()) - return QString(); + return k; QByteArray hex = QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Sha1).toHex(); @@ -111,13 +116,16 @@ QString QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon::IpcT // 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; + 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. + QString native = u'/' + QLatin1StringView(hex).left(SHM_NAME_MAX - 1); + QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, native, key); } - // 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'/' + QLatin1StringView(hex).left(SHM_NAME_MAX - 1); + return k; #endif } @@ -141,38 +149,51 @@ QString QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon::IpcT switch (type) { case QNativeIpcKey::Type::Windows: - if (!isIpcSupported(ipcType, QNativeIpcKey::Type::Windows)) - return QString(); - return result; + if (isIpcSupported(ipcType, QNativeIpcKey::Type::Windows)) + QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, result, key); + return k; case QNativeIpcKey::Type::PosixRealtime: - if (!isIpcSupported(ipcType, QNativeIpcKey::Type::PosixRealtime)) - return QString(); - return result.prepend(u'/'); + result.prepend(u'/'); + if (isIpcSupported(ipcType, QNativeIpcKey::Type::PosixRealtime)) + QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, result, key); + return k; case QNativeIpcKey::Type::SystemV: break; } - if (!isIpcSupported(ipcType, QNativeIpcKey::Type::SystemV)) - return QString(); - return 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; } -QString QtIpcCommon::platformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType, - QNativeIpcKey::Type type) +/*! + \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) { + QNativeIpcKey k(type); if (key.isEmpty()) - return key; + return k; switch (type) { case QNativeIpcKey::Type::PosixRealtime: - if (!isIpcSupported(ipcType, QNativeIpcKey::Type::PosixRealtime)) - return QString(); + if (isIpcSupported(ipcType, QNativeIpcKey::Type::PosixRealtime)) { #ifdef SHM_NAME_MAX - // The shared memory name limit on Apple platforms is very low (30 - // characters), so we have to cut it down to avoid ENAMETOOLONG. We - // hope that there won't be too many collisions... - return u'/' + QStringView(key).left(SHM_NAME_MAX - 1); + // The shared memory name limit on Apple platforms is very low (30 + // characters), so we have to cut it down to avoid ENAMETOOLONG. We + // hope that there won't be too many collisions... + k.setNativeKey(u'/' + QStringView(key).left(SHM_NAME_MAX - 1)); +#else + k.setNativeKey(u'/' + key); #endif - return u'/' + key; + } + return k; case QNativeIpcKey::Type::Windows: if (isIpcSupported(ipcType, QNativeIpcKey::Type::Windows)) { @@ -194,25 +215,27 @@ QString QtIpcCommon::platformSafeKey(const QString &key, QtIpcCommon::IpcType ip } QString result = prefix + mid + payload; -#ifdef MAX_PATH +#ifdef Q_OS_WINDOWS result.truncate(MAX_PATH); #endif - return result; + k.setNativeKey(result); } - return QString(); + return k; case QNativeIpcKey::Type::SystemV: break; } // System V - if (!isIpcSupported(ipcType, QNativeIpcKey::Type::SystemV)) - return QString(); - if (key.startsWith(u'/')) - return key; - - QString baseDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); - return baseDir + u'/' + key; + if (isIpcSupported(ipcType, QNativeIpcKey::Type::SystemV)) { + if (key.startsWith(u'/')) { + k.setNativeKey(key); + } else { + QString baseDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); + k.setNativeKey(baseDir + u'/' + key); + } + } + return k; } /*! @@ -221,6 +244,8 @@ QString QtIpcCommon::platformSafeKey(const QString &key, QtIpcCommon::IpcType ip \since 6.6 \brief The QNativeIpcKey class holds a native key used by QSystemSemaphore and QSharedMemory. + \compares equality + The \l QSharedMemory and \l QSystemSemaphore classes identify their resource using a system-wide identifier known as a "key". The low-level key value as well as the key type are encapsulated in Qt using the \l @@ -343,6 +368,11 @@ QNativeIpcKey::Type QNativeIpcKey::defaultTypeForOs_internal() noexcept } #endif +/*! + \fn QNativeIpcKey::QNativeIpcKey() noexcept + + Constructs a QNativeIpcKey object of type \l DefaultTypeForOs with an empty key. +*/ /*! \fn QNativeIpcKey::QNativeIpcKey(Type type) noexcept @@ -360,19 +390,26 @@ QNativeIpcKey::Type QNativeIpcKey::defaultTypeForOs_internal() noexcept Copies or moves the content of \a other. */ -void QNativeIpcKey::copy_internal(const QNativeIpcKey &) +void QNativeIpcKey::copy_internal(const QNativeIpcKey &other) { - Q_UNREACHABLE(); + d = new QNativeIpcKeyPrivate(*other.d); } void QNativeIpcKey::move_internal(QNativeIpcKey &&) noexcept { - Q_UNREACHABLE(); + // inline code already moved properly, nothing for us to do here } -QNativeIpcKey &QNativeIpcKey::assign_internal(const QNativeIpcKey &) +QNativeIpcKey &QNativeIpcKey::assign_internal(const QNativeIpcKey &other) { - Q_UNREACHABLE_RETURN(*this); + Q_ASSERT(d || other.d); // only 3 cases to handle + if (d && !other.d) + *d = {}; + else if (d) + *d = *other.d; + else + d = new QNativeIpcKeyPrivate(*other.d); + return *this; } /*! @@ -382,16 +419,22 @@ QNativeIpcKey &QNativeIpcKey::assign_internal(const QNativeIpcKey &) */ void QNativeIpcKey::destroy_internal() noexcept { - Q_ASSERT(isSlowPath()); - Q_UNREACHABLE(); + delete d; } /*! \fn QNativeIpcKey::swap(QNativeIpcKey &other) noexcept - \fn swap(QNativeIpcKey &lhs, QNativeIpcKey &rhs) noexcept - Swaps the native IPC key and type \a other with this object, or \a lhs with - \a rhs. This operation is very fast and never fails. + Swaps the native IPC key and type \a other with this object. + This operation is very fast and never fails. +*/ + +/*! + \fn swap(QNativeIpcKey &value1, QNativeIpcKey &value2) noexcept + \relates QNativeIpcKey + + Swaps the native IPC key and type \a value1 with \a value2. + This operation is very fast and never fails. */ /*! @@ -422,11 +465,6 @@ void QNativeIpcKey::destroy_internal() noexcept \sa nativeKey(), setType() */ -QNativeIpcKey::Type QNativeIpcKey::type_internal() const noexcept -{ - Q_ASSERT(isSlowPath()); - Q_UNREACHABLE_RETURN({}); -} /*! \fn QNativeIpcKey::setType(Type type) @@ -435,9 +473,9 @@ QNativeIpcKey::Type QNativeIpcKey::type_internal() const noexcept \sa type(), setNativeKey() */ -void QNativeIpcKey::setType_internal(Type) +void QNativeIpcKey::setType_internal(Type type) { - Q_UNREACHABLE(); + Q_UNUSED(type); } /*! @@ -455,6 +493,28 @@ void QNativeIpcKey::setType_internal(Type) \sa nativeKey(), setType() */ +void QNativeIpcKey::setNativeKey_internal(const QString &) +{ + d->legacyKey_.clear(); +} + +/*! + \fn size_t QNativeIpcKey::qHash(const QNativeIpcKey &ipcKey) noexcept + + Returns the hash value for \a ipcKey, using a default seed of \c 0. +*/ + +/*! + \fn size_t QNativeIpcKey::qHash(const QNativeIpcKey &ipcKey, size_t seed) noexcept + + Returns the hash value for \a ipcKey, using \a seed to seed the calculation. +*/ +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()); +} /*! \fn bool QNativeIpcKey::operator==(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept @@ -462,9 +522,9 @@ void QNativeIpcKey::setType_internal(Type) Returns true if the \a lhs and \a rhs objects hold the same (or different) contents. */ -int QNativeIpcKey::compare_internal(const QNativeIpcKey &, const QNativeIpcKey &) noexcept +int QNativeIpcKey::compare_internal(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept { - Q_UNREACHABLE_RETURN(0); + return (QNativeIpcKeyPrivate::legacyKey(lhs) == QNativeIpcKeyPrivate::legacyKey(rhs)) ? 0 : 1; } /*! @@ -479,28 +539,27 @@ int QNativeIpcKey::compare_internal(const QNativeIpcKey &, const QNativeIpcKey & */ QString QNativeIpcKey::toString() const { - Q_ASSERT(!isSlowPath()); - QString prefix = typeToString(typeAndFlags.type); + QString prefix = typeToString(type()); if (prefix.isEmpty()) { Q_ASSERT(prefix.isNull()); return prefix; } - prefix += u':'; QString copy = nativeKey(); copy.replace(u'%', "%25"_L1); if (copy.startsWith("//"_L1)) copy.replace(0, 2, u"/%2F"_s); // ensure it's parsed as a URL path - const ushort recodeActions[] = { - '\\' | 0, // decode - '?' | 0x200, // encode - '#' | 0x200, // encode - 0 - }; - if (!qt_urlRecode(prefix, copy, QUrl::DecodeReserved, recodeActions)) - prefix += copy; - return prefix; + 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); } /*! @@ -515,14 +574,11 @@ QString QNativeIpcKey::toString() const */ QNativeIpcKey QNativeIpcKey::fromString(const QString &text) { - // this duplicates QUrlPrivate::parse a little + QUrl u(text, QUrl::TolerantMode); Type invalidType = {}; - qsizetype colon = text.indexOf(u':'); - if (colon < 0) - return QNativeIpcKey(invalidType); - - Type type = stringToType(QStringView(text).left(colon)); - if (type == invalidType) + Type type = stringToType(u.scheme()); + if (type == invalidType || !u.isValid() || !u.userInfo().isEmpty() || !u.host().isEmpty() + || u.port() != -1) return QNativeIpcKey(invalidType); QNativeIpcKey result(QString(), type); @@ -530,20 +586,20 @@ QNativeIpcKey QNativeIpcKey::fromString(const QString &text) return QNativeIpcKey(invalidType); // decode the payload - QStringView payload = QStringView(text).sliced(colon + 1); - if (qsizetype pos = payload.indexOf(u'?'); pos >= 0) - payload.truncate(pos); - if (qsizetype pos = payload.indexOf(u'#'); pos >= 0) - payload.truncate(pos); - - // qt_urlRecode requires a two-step decoding for non-ASCII content - QString nativeKey, intermediate; - if (qt_urlRecode(intermediate, payload, QUrl::PrettyDecoded)) - payload = intermediate; - if (!qt_urlRecode(nativeKey, payload, QUrl::FullyDecoded)) - nativeKey = payload.toString(); - - result.setNativeKey(nativeKey); + 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; } |