diff --git a/src/blackcore/context/contextsimulatorimpl.cpp b/src/blackcore/context/contextsimulatorimpl.cpp index e426b2945..132f21d9f 100644 --- a/src/blackcore/context/contextsimulatorimpl.cpp +++ b/src/blackcore/context/contextsimulatorimpl.cpp @@ -203,8 +203,7 @@ namespace BlackCore { // ever used with XPlane const QString pluginDir = CXPlaneUtil::pluginDirFromRootDir(m_simulatorSettings.getSimulatorDirectoryOrDefault(CSimulatorInfo::XPLANE)); - const QDir dir(pluginDir); - if (dir.exists()) + if (CDirectoryUtils::isDirExisting(pluginDir)) { // only check if we are on a XP machine const QStringList conflicts = CXPlaneUtil::findConflictingPlugins(pluginDir); diff --git a/src/blackcore/simulatorcommon.cpp b/src/blackcore/simulatorcommon.cpp index 87d4464ab..8f8b02586 100644 --- a/src/blackcore/simulatorcommon.cpp +++ b/src/blackcore/simulatorcommon.cpp @@ -479,7 +479,7 @@ namespace BlackCore if (part2 == "show") { const QDir dir(CInterpolationLogger::getLogDirectory()); - if (dir.exists()) + if (CDirectoryUtils::isDirExisting(dir)) { const QUrl dirUrl = QUrl::fromLocalFile(dir.absolutePath()); QDesktopServices::openUrl(dirUrl); // show dir in browser diff --git a/src/blackmisc/directoryutils.cpp b/src/blackmisc/directoryutils.cpp index e76ea349d..9d1f14ece 100644 --- a/src/blackmisc/directoryutils.cpp +++ b/src/blackmisc/directoryutils.cpp @@ -9,9 +9,9 @@ //! \cond PRIVATE -#include "blackmisc/directoryutils.h" -#include "blackmisc/fileutils.h" -#include "blackmisc/range.h" +#include "directoryutils.h" +#include "fileutils.h" +#include "range.h" #include "blackconfig/buildconfig.h" #include #include @@ -416,7 +416,7 @@ namespace BlackMisc { if (testDir.isEmpty()) { return false; } const QDir dir(testDir); - if (!dir.exists()) { return false; } + if (!CDirectoryUtils::isDirExisting(dir)) { return false; } return !dir.isEmpty(); } @@ -438,6 +438,33 @@ namespace BlackMisc return dirs; } + bool CDirectoryUtils::isDirExisting(const QString &path) + { + if (!CBuildConfig::isRunningOnWindowsNtPlatform()) + { + const QDir dir(path); + return dir.exists(); + } + + // Windows + if (!CFileUtils::isWindowsUncPath(path)) + { + const QDir dir(path); + return dir.exists(); + } + const QString machine(CFileUtils::windowsUncMachine(path)); + if (!CFileUtils::canPingUncMachine(machine)) { return false; } + + const QDir dir(path); + return dir.exists(); + } + + bool CDirectoryUtils::isDirExisting(const QDir &dir) + { + if (!CFileUtils::isWindowsUncPath(dir.absolutePath())) { return dir.exists(); } + return CDirectoryUtils::isDirExisting(dir.absolutePath()); + } + QSet CDirectoryUtils::fileNamesToQSet(const QFileInfoList &fileInfoList) { QSet sl; diff --git a/src/blackmisc/directoryutils.h b/src/blackmisc/directoryutils.h index 673a105e4..469a39ef5 100644 --- a/src/blackmisc/directoryutils.h +++ b/src/blackmisc/directoryutils.h @@ -18,6 +18,7 @@ #include #include #include +#include #include namespace BlackMisc @@ -144,6 +145,14 @@ namespace BlackMisc //! Get the existing directories static QStringList getExistingUnemptyDirectories(const QStringList &directories); + //! Directory existing? Also checking UNC paths upfront. + //! \remark Motivation: if an UNC cannot be accessed (e.g. machine is down) it can take very long before functions like QDir respond + //! \remark for non-UNC paths it is the same as the QDir checks + //! @{ + static bool isDirExisting(const QString &path); + static bool isDirExisting(const QDir &dir); + //! @} + //! Result of directory comparison struct DirComparison { diff --git a/src/blackmisc/fileutils.cpp b/src/blackmisc/fileutils.cpp index d330ba156..aadf9d948 100644 --- a/src/blackmisc/fileutils.cpp +++ b/src/blackmisc/fileutils.cpp @@ -7,9 +7,10 @@ * contained in the LICENSE file. */ +#include "blackmisc/network/networkutils.h" +#include "blackmisc/math/mathutils.h" #include "blackmisc/worker.h" #include "blackmisc/fileutils.h" -#include "blackmisc/math/mathutils.h" #include "blackconfig/buildconfig.h" #include @@ -21,10 +22,12 @@ #include #include #include +#include #include using namespace BlackConfig; using namespace BlackMisc::Math; +using namespace BlackMisc::Network; namespace BlackMisc { @@ -383,6 +386,59 @@ namespace BlackMisc return fixedPaths; } + bool CFileUtils::isWindowsUncPath(const QString &filePath) + { + if (filePath.startsWith("//") || filePath.startsWith("\\\\")) { return true; } + if (!CBuildConfig::isRunningOnWindowsNtPlatform()) { return false; } // "/tmp" is valid on Unix/Mac + + // Windows here + const QString fp = fixWindowsUncPath(filePath); + return (fp.startsWith("//") || fp.startsWith("\\\\")); + } + + QString CFileUtils::windowsUncMachine(const QString &filePath) + { + if (!CFileUtils::isWindowsUncPath(filePath)) { return QStringLiteral(""); } + QString f = filePath; + f.replace("\\", "/"); + f.replace("//", ""); + if (f.startsWith("/")) { f = f.mid(1); } + const int i = f.indexOf('/'); + if (i < 0) { return f; } + return f.left(i); + } + + bool CFileUtils::canPingUncMachine(const QString &machine) + { + static QMap good; + static QMap bad; + + if (machine.isEmpty()) { return false; } + const QString m = machine.toLower(); + if (good.contains(m)) { return true; } // good ones we only test once + if (bad.contains(m)) + { + const qint64 ts = bad.value(m); + const qint64 dt = QDateTime::currentSecsSinceEpoch() - ts; + if (dt < 1000 * 60) { return false; } // still bad + + // outdated, test again + } + + const bool p = CNetworkUtils::canPing(m); + if (p) + { + good.insert(m, QDateTime::currentSecsSinceEpoch()); + bad.remove(m); + } + else + { + bad.insert(m, QDateTime::currentSecsSinceEpoch()); + good.remove(m); + } + return p; + } + QString CFileUtils::toWindowsLocalPath(const QString &path) { QString p = CFileUtils::fixWindowsUncPath(path); diff --git a/src/blackmisc/fileutils.h b/src/blackmisc/fileutils.h index 41d02fc28..901e73607 100644 --- a/src/blackmisc/fileutils.h +++ b/src/blackmisc/fileutils.h @@ -147,6 +147,15 @@ namespace BlackMisc //! Fix UNC file paths static QStringList fixWindowsUncPaths(const QStringList &filePaths); + //! Windows UNC path? + static bool isWindowsUncPath(const QString &filePath); + + //! Machine in Windows UNC path + static QString windowsUncMachine(const QString &filePath); + + //! Can connect the UNC machine + static bool canPingUncMachine(const QString &machine); + //! To Windows path using "\" delimiter static QString toWindowsLocalPath(const QString &path); diff --git a/src/blackmisc/network/networkutils.cpp b/src/blackmisc/network/networkutils.cpp index bab88e80d..aa8d3bae1 100644 --- a/src/blackmisc/network/networkutils.cpp +++ b/src/blackmisc/network/networkutils.cpp @@ -74,7 +74,14 @@ namespace BlackMisc } process.start(); process.waitForFinished(); - return process.exitCode() == 0; + const int rc = process.exitCode(); + if (rc != 0) { return false; } + + const QString std = process.readAllStandardOutput(); + const QString err = process.readAllStandardError(); + if (std.contains("unreachable", Qt::CaseInsensitive)) { return false; } + if (err.contains("unreachable", Qt::CaseInsensitive)) { return false; } + return true; } bool CNetworkUtils::canPing(const CUrl &url) @@ -92,7 +99,7 @@ namespace BlackMisc if (address.isNull()) { continue; } if (address.protocol() == QAbstractSocket::IPv4Protocol) { - QString a = address.toString(); + const QString a = address.toString(); ips.append(a); } } diff --git a/src/blackmisc/simulation/xplane/xplaneutil.cpp b/src/blackmisc/simulation/xplane/xplaneutil.cpp index a37693bca..acc8626ef 100644 --- a/src/blackmisc/simulation/xplane/xplaneutil.cpp +++ b/src/blackmisc/simulation/xplane/xplaneutil.cpp @@ -157,8 +157,8 @@ namespace BlackMisc QStringList CXPlaneUtil::pluginSubdirectories(const QString &pluginDir) { const QString dirName = pluginDir.isEmpty() ? xplaneRootDir() : pluginDir; + if (!CDirectoryUtils::isDirExisting(dirName)) { return QStringList(); } const QDir dir(dirName); - if (!dir.exists()) { return QStringList(); } return dir.entryList(QDir::Dirs, QDir::Name | QDir::IgnoreCase); } @@ -180,7 +180,7 @@ namespace BlackMisc if (!rootDir.isEmpty()) { const QString xswiftbusDir = CFileUtils::appendFilePathsAndFixUnc(CXPlaneUtil::pluginDirFromRootDir(xplaneRootDir), CXPlaneUtil::xswiftbusPathName()); - if (QDir(xswiftbusDir).exists()) + if (CDirectoryUtils::isDirExisting(xswiftbusDir)) { return xswiftbusDir; } @@ -197,7 +197,7 @@ namespace BlackMisc if (!rootDir.isEmpty()) { const QString xswiftbusLegacy = CFileUtils::appendFilePathsAndFixUnc(xplaneRootDir, legacyPath); - if (QDir(xswiftbusLegacy).exists()) + if (CDirectoryUtils::isDirExisting(xswiftbusLegacy)) { return xswiftbusLegacy; }