From 64e438ed8392e945304818514124aa57d596b0ba Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 14 Sep 2021 14:14:15 +0200 Subject: [PATCH] Docker: Implement directoryEntries alternative Use 'find' on a docker image to search for diretory entries if it is available. If the find call fails we still may fallback to using 'ls'. This silences currently active soft asserts regarding the usage of sort or file filters when we are able to use 'find'. Only support the most common and currently used options and bark if some unsupported option is in use. Task-number: QTCREATORBUG-26258 Change-Id: I9a082ea7aca1b6db7dcb668cfe40ed0ed48cd567 Reviewed-by: Christian Stenger Reviewed-by: David Schulz --- src/plugins/docker/dockerdevice.cpp | 78 +++++++++++++++++++++++++++++ src/plugins/docker/dockerdevice.h | 4 ++ 2 files changed, 82 insertions(+) diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index f72b2559eec..4fed5a0792a 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -333,6 +333,8 @@ public: QFileSystemWatcher m_mergedDirWatcher; Environment m_cachedEnviroment; + + bool m_useFind = true; // prefer find over ls and hacks, but be able to use ls as fallback }; class DockerDeviceWidget final : public IDeviceWidget @@ -1311,6 +1313,75 @@ bool DockerDevice::setPermissions(const FilePath &filePath, QFileDevice::Permiss return false; } +FilePaths DockerDevice::findFilesWithFind(const FilePath &filePath, + const QStringList &nameFilters, + QDir::Filters filters, + QDir::SortFlags sort) const +{ + QTC_CHECK(filePath.isAbsolutePath()); + QStringList arguments{filePath.path(), "-maxdepth", "1"}; + if (filters & QDir::NoSymLinks) + arguments.prepend("-H"); + else + arguments.prepend("-L"); + + + QStringList filterOptions; + if (filters & QDir::Dirs) + filterOptions << "-type" << "d"; + if (filters & QDir::Files) { + if (!filterOptions.isEmpty()) + filterOptions << "-o"; + filterOptions << "-type" << "f"; + } + + if (filters & QDir::Readable) + filterOptions << "-readable"; + if (filters & QDir::Writable) + filterOptions << "-writable"; + if (filters & QDir::Executable) + filterOptions << "-executable"; + + QTC_CHECK(filters ^ QDir::AllDirs); + QTC_CHECK(filters ^ QDir::Drives); + QTC_CHECK(filters ^ QDir::NoDot); + QTC_CHECK(filters ^ QDir::NoDotDot); + QTC_CHECK(filters ^ QDir::Hidden); + QTC_CHECK(filters ^ QDir::System); + + const QString nameOption = (filters & QDir::CaseSensitive) ? QString{"-name"} + : QString{"-iname"}; + if (!nameFilters.isEmpty()) { + filterOptions << nameOption << nameFilters.first(); + const QRegularExpression oneChar("\\[.*?\\]"); + for (int i = 1, len = nameFilters.size(); i < len; ++i) { + QString current = nameFilters.at(i); + current.replace(oneChar, "?"); // BAD! but still better than nothing + filterOptions << "-o" << nameOption << current; + } + } + arguments << filterOptions; + const QString output = d->outputForRunInShell({"find", arguments}); + if (!output.isEmpty() && !output.startsWith(filePath.path())) { // missing find, unknown option + LOG("Setting 'do not use find'" << output.left(output.indexOf('\n'))); + d->m_useFind = false; + return {}; + } + + QStringList entries = output.split("\n", Qt::SkipEmptyParts); + if (sort & QDir::Name) + entries.sort(filters & QDir::CaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); + QTC_CHECK(sort == QDir::Name || sort == QDir::NoSort || sort == QDir::Unsorted); + + // strip out find messages + entries = Utils::filtered(entries, + [](const QString &entry) { return !entry.startsWith("find: "); }); + FilePaths result; + for (const QString &entry : qAsConst(entries)) + result.append(FilePath::fromString(entry).onDevice(filePath)); + return result; +} + static FilePaths filterEntriesHelper(const FilePath &base, const QStringList &entries, const QStringList &nameFilters, @@ -1360,6 +1431,13 @@ FilePaths DockerDevice::directoryEntries(const FilePath &filePath, }); } + if (d->m_useFind) { + const FilePaths result = findFilesWithFind(filePath, nameFilters, filters, sort); + if (d->m_useFind) + return result; + } + + // if we do not have find - use ls as fallback const QString output = d->outputForRunInShell({"ls", {"-1", "-b", "--", filePath.path()}}); QStringList entries = output.split('\n', Qt::SkipEmptyParts); return filterEntriesHelper(filePath, entries, nameFilters, filters, sort); diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h index 890bc19aca8..35eac6e3905 100644 --- a/src/plugins/docker/dockerdevice.h +++ b/src/plugins/docker/dockerdevice.h @@ -117,6 +117,10 @@ public: Utils::FilePath mapFromLocalAccess(const QString &filePath) const; private: + Utils::FilePaths findFilesWithFind(const Utils::FilePath &filePath, + const QStringList &nameFilters, + QDir::Filters filters, + QDir::SortFlags sort) const; void fromMap(const QVariantMap &map) final; QVariantMap toMap() const final;