diff options
21 files changed, 1234 insertions, 1738 deletions
diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index 1d7099bc3a9..a4135401651 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -28,6 +28,7 @@ add_qtc_library(Utils delegates.cpp delegates.h detailsbutton.cpp detailsbutton.h detailswidget.cpp detailswidget.h + devicefileaccess.cpp devicefileaccess.h deviceshell.cpp deviceshell.h differ.cpp differ.h displayname.cpp displayname.h diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp new file mode 100644 index 00000000000..6b07edaf62b --- /dev/null +++ b/src/libs/utils/devicefileaccess.cpp @@ -0,0 +1,862 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "devicefileaccess.h" + +#include "algorithm.h" +#include "qtcassert.h" +#include "hostosinfo.h" + +#include <QCoreApplication> +#include <QRegularExpression> +#include <QStorageInfo> + +namespace Utils { + +// DeviceFileAccess + +DeviceFileAccess::~DeviceFileAccess() = default; + +QString DeviceFileAccess::mapToDevicePath(const FilePath &filePath) const +{ + return filePath.path(); +} + +bool DeviceFileAccess::isExecutableFile(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::isReadableFile(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::isWritableFile(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::isReadableDirectory(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::isWritableDirectory(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::isFile(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::isDirectory(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const +{ + if (isWritableDirectory(filePath)) + return true; + return createDirectory(filePath); +} + +bool DeviceFileAccess::ensureExistingFile(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::createDirectory(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::exists(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::removeFile(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::removeRecursively(const FilePath &filePath, QString *error) const +{ + Q_UNUSED(filePath) + Q_UNUSED(error) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const +{ + Q_UNUSED(filePath) + Q_UNUSED(target) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const +{ + Q_UNUSED(filePath) + Q_UNUSED(target) + QTC_CHECK(false); + return false; +} + +OsType DeviceFileAccess::osType(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + return OsTypeOther; +} + +FilePath DeviceFileAccess::symLinkTarget(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return {}; +} + +void DeviceFileAccess::iterateDirectory( + const FilePath &filePath, + const FilePath::IterateDirCallback &callBack, + const FileFilter &filter) const +{ + Q_UNUSED(filePath) + Q_UNUSED(callBack) + Q_UNUSED(filter) + QTC_CHECK(false); +} + +std::optional<QByteArray> DeviceFileAccess::fileContents( + const FilePath &filePath, + qint64 limit, + qint64 offset) const +{ + Q_UNUSED(filePath) + Q_UNUSED(limit) + Q_UNUSED(offset) + QTC_CHECK(false); + return {}; +} + +bool DeviceFileAccess::writeFileContents( + const FilePath &filePath, + const QByteArray &data, + qint64 offset) const +{ + Q_UNUSED(filePath) + Q_UNUSED(data) + Q_UNUSED(offset) + QTC_CHECK(false); + return false; +} + +FilePathInfo DeviceFileAccess::filePathInfo(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return {}; +} + +QDateTime DeviceFileAccess::lastModified(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return {}; +} + +QFile::Permissions DeviceFileAccess::permissions(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return {}; +} + +bool DeviceFileAccess::setPermissions(const FilePath &filePath, QFile::Permissions) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +qint64 DeviceFileAccess::fileSize(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +qint64 DeviceFileAccess::bytesAvailable(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return -1; +} + +void DeviceFileAccess::asyncFileContents( + const FilePath &filePath, + const Continuation<std::optional<QByteArray>> &cont, + qint64 limit, + qint64 offset) const +{ + cont(fileContents(filePath, limit, offset)); +} + +void DeviceFileAccess::asyncWriteFileContents( + const FilePath &filePath, + const Continuation<bool> &cont, + const QByteArray &data, + qint64 offset) const +{ + cont(writeFileContents(filePath, data, offset)); +} + +void DeviceFileAccess::asyncCopyFile( + const FilePath &filePath, + const Continuation<bool> &cont, + const FilePath &target) const +{ + cont(copyFile(filePath, target)); +} + + +// DesktopDeviceFileAccess + +DesktopDeviceFileAccess::~DesktopDeviceFileAccess() = default; + +DesktopDeviceFileAccess *DesktopDeviceFileAccess::instance() +{ + static DesktopDeviceFileAccess theInstance; + return &theInstance; +} + +bool DesktopDeviceFileAccess::isExecutableFile(const FilePath &filePath) const +{ + const QFileInfo fi(filePath.path()); + return fi.isExecutable() && !fi.isDir(); +} + +bool DesktopDeviceFileAccess::isReadableFile(const FilePath &filePath) const +{ + const QFileInfo fi(filePath.path()); + return fi.exists() && fi.isFile() && fi.isReadable(); +} + +bool DesktopDeviceFileAccess::isWritableFile(const FilePath &filePath) const +{ + const QFileInfo fi(filePath.path()); + return fi.exists() && fi.isFile() && fi.isWritable(); +} + +bool DesktopDeviceFileAccess::isReadableDirectory(const FilePath &filePath) const +{ + const QFileInfo fi(filePath.path()); + return fi.exists() && fi.isDir() && fi.isReadable(); +} + +bool DesktopDeviceFileAccess::isWritableDirectory(const FilePath &filePath) const +{ + const QFileInfo fi(filePath.path()); + return fi.exists() && fi.isDir() && fi.isWritable(); +} + +bool DesktopDeviceFileAccess::isFile(const FilePath &filePath) const +{ + const QFileInfo fi(filePath.path()); + return fi.isFile(); +} + +bool DesktopDeviceFileAccess::isDirectory(const FilePath &filePath) const +{ + const QFileInfo fi(filePath.path()); + return fi.isDir(); +} + +bool DesktopDeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const +{ + const QFileInfo fi(filePath.path()); + if (fi.isDir() && fi.isWritable()) + return true; + return QDir().mkpath(filePath.path()); +} + +bool DesktopDeviceFileAccess::ensureExistingFile(const FilePath &filePath) const +{ + QFile f(filePath.path()); + if (f.exists()) + return true; + f.open(QFile::WriteOnly); + f.close(); + return f.exists(); +} + +bool DesktopDeviceFileAccess::createDirectory(const FilePath &filePath) const +{ + QDir dir(filePath.path()); + return dir.mkpath(dir.absolutePath()); +} + +bool DesktopDeviceFileAccess::exists(const FilePath &filePath) const +{ + return !filePath.isEmpty() && QFileInfo::exists(filePath.path()); +} + +bool DesktopDeviceFileAccess::removeFile(const FilePath &filePath) const +{ + return QFile::remove(filePath.path()); +} + +bool DesktopDeviceFileAccess::removeRecursively(const FilePath &filePath, QString *error) const +{ + QTC_ASSERT(!filePath.needsDevice(), return false); + QFileInfo fileInfo = filePath.toFileInfo(); + if (!fileInfo.exists() && !fileInfo.isSymLink()) + return true; + + QFile::setPermissions(fileInfo.absoluteFilePath(), fileInfo.permissions() | QFile::WriteUser); + + if (fileInfo.isDir()) { + QDir dir(fileInfo.absoluteFilePath()); + dir.setPath(dir.canonicalPath()); + if (dir.isRoot()) { + if (error) { + *error = QCoreApplication::translate("Utils::FileUtils", + "Refusing to remove root directory."); + } + return false; + } + if (dir.path() == QDir::home().canonicalPath()) { + if (error) { + *error = QCoreApplication::translate("Utils::FileUtils", + "Refusing to remove your home directory."); + } + return false; + } + + const QStringList fileNames = dir.entryList( + QDir::Files | QDir::Hidden | QDir::System | QDir::Dirs | QDir::NoDotAndDotDot); + for (const QString &fileName : fileNames) { + if (!removeRecursively(filePath / fileName, error)) + return false; + } + if (!QDir::root().rmdir(dir.path())) { + if (error) { + *error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove directory \"%1\".") + .arg(filePath.toUserOutput()); + } + return false; + } + } else { + if (!QFile::remove(filePath.toString())) { + if (error) { + *error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove file \"%1\".") + .arg(filePath.toUserOutput()); + } + return false; + } + } + return true; +} + +bool DesktopDeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const +{ + return QFile::copy(filePath.path(), target.path()); +} + +bool DesktopDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const +{ + return QFile::rename(filePath.path(), target.path()); +} + +FilePathInfo DesktopDeviceFileAccess::filePathInfo(const FilePath &filePath) const +{ + FilePathInfo result; + + QFileInfo fi(filePath.path()); + result.fileSize = fi.size(); + result.lastModified = fi.lastModified(); + result.fileFlags = (FilePathInfo::FileFlag) int(fi.permissions()); + + if (fi.isDir()) + result.fileFlags |= FilePathInfo::DirectoryType; + if (fi.isFile()) + result.fileFlags |= FilePathInfo::FileType; + if (fi.exists()) + result.fileFlags |= FilePathInfo::ExistsFlag; + if (fi.isSymbolicLink()) + result.fileFlags |= FilePathInfo::LinkType; + if (fi.isBundle()) + result.fileFlags |= FilePathInfo::BundleType; + if (fi.isHidden()) + result.fileFlags |= FilePathInfo::HiddenFlag; + if (fi.isRoot()) + result.fileFlags |= FilePathInfo::RootFlag; + + return result; +} + +FilePath DesktopDeviceFileAccess::symLinkTarget(const FilePath &filePath) const +{ + const QFileInfo info(filePath.path()); + if (!info.isSymLink()) + return {}; + return FilePath::fromString(info.symLinkTarget()); +} + +void DesktopDeviceFileAccess::iterateDirectory( + const FilePath &filePath, + const FilePath::IterateDirCallback &callBack, + const FileFilter &filter) const +{ + QDirIterator it(filePath.path(), filter.nameFilters, filter.fileFilters, filter.iteratorFlags); + while (it.hasNext()) { + const FilePath path = FilePath::fromString(it.next()); + bool res = false; + if (callBack.index() == 0) + res = std::get<0>(callBack)(path); + else + res = std::get<1>(callBack)(path, path.filePathInfo()); + if (!res) + return; + } +} + +std::optional<QByteArray> DesktopDeviceFileAccess::fileContents( + const FilePath &filePath, + qint64 limit, + qint64 offset) const +{ + const QString path = filePath.path(); + QFile f(path); + if (!f.exists()) + return {}; + + if (!f.open(QFile::ReadOnly)) + return {}; + + if (offset != 0) + f.seek(offset); + + if (limit != -1) + return f.read(limit); + + return f.readAll(); +} + +bool DesktopDeviceFileAccess::writeFileContents( + const FilePath &filePath, + const QByteArray &data, + qint64 offset) const +{ + QFile file(filePath.path()); + QTC_ASSERT(file.open(QFile::WriteOnly | QFile::Truncate), return false); + if (offset != 0) + file.seek(offset); + qint64 res = file.write(data); + return res == data.size(); +} + +QDateTime DesktopDeviceFileAccess::lastModified(const FilePath &filePath) const +{ + return QFileInfo(filePath.path()).lastModified(); +} + +QFile::Permissions DesktopDeviceFileAccess::permissions(const FilePath &filePath) const +{ + return QFileInfo(filePath.path()).permissions(); +} + +bool DesktopDeviceFileAccess::setPermissions(const FilePath &filePath, + QFile::Permissions permissions) const +{ + return QFile(filePath.path()).setPermissions(permissions); +} + +qint64 DesktopDeviceFileAccess::fileSize(const FilePath &filePath) const +{ + return QFileInfo(filePath.path()).size(); +} + +qint64 DesktopDeviceFileAccess::bytesAvailable(const FilePath &filePath) const +{ + return QStorageInfo(filePath.path()).bytesAvailable(); +} + +OsType DesktopDeviceFileAccess::osType(const FilePath &filePath) const +{ + Q_UNUSED(filePath); + return HostOsInfo::hostOs(); +} + + +// UnixDeviceAccess + +UnixDeviceFileAccess::~UnixDeviceFileAccess() = default; + +bool UnixDeviceFileAccess::runInShellSuccess( + const QString &executable, + const QStringList &args, + const QByteArray &stdInData) const +{ + return runInShell(executable, args, stdInData).exitCode == 0; +} + +bool UnixDeviceFileAccess::isExecutableFile(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("test", {"-x", path}); +} + +bool UnixDeviceFileAccess::isReadableFile(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("test", {"-r", path, "-a", "-f", path}); +} + +bool UnixDeviceFileAccess::isWritableFile(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("test", {"-w", path, "-a", "-f", path}); +} + +bool UnixDeviceFileAccess::isReadableDirectory(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("test", {"-r", path, "-a", "-d", path}); +} + +bool UnixDeviceFileAccess::isWritableDirectory(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("test", {"-w", path, "-a", "-d", path}); +} + +bool UnixDeviceFileAccess::isFile(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("test", {"-f", path}); +} + +bool UnixDeviceFileAccess::isDirectory(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("test", {"-d", path}); +} + +bool UnixDeviceFileAccess::ensureExistingFile(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("touch", {path}); +} + +bool UnixDeviceFileAccess::createDirectory(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("mkdir", {"-p", path}); +} + +bool UnixDeviceFileAccess::exists(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("test", {"-e", path}); +} + +bool UnixDeviceFileAccess::removeFile(const FilePath &filePath) const +{ + return runInShellSuccess("rm", {filePath.path()}); +} + +bool UnixDeviceFileAccess::removeRecursively(const FilePath &filePath, QString *error) const +{ + QTC_ASSERT(filePath.path().startsWith('/'), return false); + + const QString path = filePath.cleanPath().path(); + // We are expecting this only to be called in a context of build directories or similar. + // Chicken out in some cases that _might_ be user code errors. + QTC_ASSERT(path.startsWith('/'), return false); + int levelsNeeded = path.startsWith("/home/") ? 4 : 3; + if (path.startsWith("/tmp/")) + levelsNeeded = 2; + QTC_ASSERT(path.count('/') >= levelsNeeded, return false); + + RunResult result = runInShell("rm", {"-rf", "--", path}); + if (error) + *error = QString::fromUtf8(result.stdErr); + return result.exitCode == 0; +} + +bool UnixDeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const +{ + return runInShellSuccess("cp", {filePath.path(), target.path()}); +} + +bool UnixDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const +{ + return runInShellSuccess("mv", {filePath.path(), target.path()}); +} + +FilePath UnixDeviceFileAccess::symLinkTarget(const FilePath &filePath) const +{ + const RunResult result = runInShell("readlink", {"-n", "-e", filePath.path()}); + const QString out = QString::fromUtf8(result.stdOut); + return out.isEmpty() ? FilePath() : filePath.withNewPath(out); +} + +std::optional<QByteArray> UnixDeviceFileAccess::fileContents( + const FilePath &filePath, + qint64 limit, + qint64 offset) const +{ + QStringList args = {"if=" + filePath.path(), "status=none"}; + if (limit > 0 || offset > 0) { + const qint64 gcd = std::gcd(limit, offset); + args += QString("bs=%1").arg(gcd); + args += QString("count=%1").arg(limit / gcd); + args += QString("seek=%1").arg(offset / gcd); + } + + const RunResult r = runInShell("dd", args); + + if (r.exitCode != 0) + return {}; + + return r.stdOut; +} + +bool UnixDeviceFileAccess::writeFileContents( + const FilePath &filePath, + const QByteArray &data, + qint64 offset) const +{ + QStringList args = {"of=" + filePath.path()}; + if (offset != 0) { + args.append("bs=1"); + args.append(QString("seek=%1").arg(offset)); + } + return runInShellSuccess("dd", args, data); +} + +OsType UnixDeviceFileAccess::osType(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + return OsTypeLinux; +} + +QDateTime UnixDeviceFileAccess::lastModified(const FilePath &filePath) const +{ + const RunResult result = runInShell("stat", {"-L", "-c", "%Y", filePath.path()}); + qint64 secs = result.stdOut.toLongLong(); + const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC); + return dt; +} + +QFile::Permissions UnixDeviceFileAccess::permissions(const FilePath &filePath) const +{ + const RunResult result = runInShell("stat", {"-L", "-c", "%a", filePath.path()}); + const uint bits = result.stdOut.toUInt(nullptr, 8); + QFileDevice::Permissions perm = {}; +#define BIT(n, p) if (bits & (1<<n)) perm |= QFileDevice::p + BIT(0, ExeOther); + BIT(1, WriteOther); + BIT(2, ReadOther); + BIT(3, ExeGroup); + BIT(4, WriteGroup); + BIT(5, ReadGroup); + BIT(6, ExeUser); + BIT(7, WriteUser); + BIT(8, ReadUser); +#undef BIT + return perm; +} + +bool UnixDeviceFileAccess::setPermissions(const FilePath &filePath, QFile::Permissions perms) const +{ + const int flags = int(perms); + return runInShellSuccess("chmod", {QString::number(flags, 16), filePath.path()}); +} + +qint64 UnixDeviceFileAccess::fileSize(const FilePath &filePath) const +{ + const RunResult result = runInShell("stat", {"-L", "-c", "%s", filePath.path()}); + return result.stdOut.toLongLong(); +} + +qint64 UnixDeviceFileAccess::bytesAvailable(const FilePath &filePath) const +{ + const RunResult result = runInShell("df", {"-k", filePath.path()}); + return FileUtils::bytesAvailableFromDFOutput(result.stdOut); +} + +FilePathInfo UnixDeviceFileAccess::filePathInfo(const FilePath &filePath) const +{ + const RunResult stat = runInShell("stat", {"-L", "-c", "%f %Y %s", filePath.path()}); + return FileUtils::filePathInfoFromTriple(QString::fromLatin1(stat.stdOut)); +} + +// returns whether 'find' could be used. +bool UnixDeviceFileAccess::iterateWithFind( + const FilePath &filePath, + const FileFilter &filter, + const FilePath::IterateDirCallback &callBack) const +{ + QTC_CHECK(filePath.isAbsolutePath()); + + QStringList arguments = filter.asFindArguments(filePath.path()); + + // TODO: Using stat -L will always return the link target, not the link itself. + // We may wan't to add the information that it is a link at some point. + if (callBack.index() == 1) + arguments.append(R"(-exec echo -n \"{}\"" " \; -exec stat -L -c "%f %Y %s" "{}" \;)"); + + const RunResult result = runInShell("find", arguments); + const QString out = QString::fromUtf8(result.stdOut); + if (result.exitCode != 0) { + // Find returns non-zero exit code for any error it encounters, even if it finds some files. + + if (!out.startsWith('"' + filePath.path())) { + if (!filePath.exists()) // File does not exist, so no files to find. + return true; + + // If the output does not start with the path we are searching in, find has failed. + // Possibly due to unknown options. + return false; + } + } + + QStringList entries = out.split("\n", Qt::SkipEmptyParts); + if (entries.isEmpty()) + return true; + + const auto toFilePath = [&filePath, &callBack](const QString &entry) -> bool { + if (callBack.index() == 0) + return std::get<0>(callBack)(filePath.withNewPath(entry)); + + const QString fileName = entry.mid(1, entry.lastIndexOf('\"') - 1); + const QString infos = entry.mid(fileName.length() + 3); + + const FilePathInfo fi = FileUtils::filePathInfoFromTriple(infos); + if (!fi.fileFlags) + return true; + + const FilePath fp = filePath.withNewPath(fileName); + return std::get<1>(callBack)(fp, fi); + }; + + // Remove the first line, this can be the directory we are searching in. + // as long as we do not specify "mindepth > 0" + if (entries.front() == filePath.path()) + entries.pop_front(); + + for (const QString &entry : entries) { + if (!toFilePath(entry)) + break; + } + + return true; +} + +void UnixDeviceFileAccess::findUsingLs( + const QString ¤t, + const FileFilter &filter, + QStringList *found) const +{ + const RunResult result = runInShell("ls", {"-1", "-p", "--", current}); + const QStringList entries = QString::fromUtf8(result.stdOut).split('\n', Qt::SkipEmptyParts); + for (QString entry : entries) { + const QChar last = entry.back(); + if (last == '/') { + entry.chop(1); + if (filter.iteratorFlags.testFlag(QDirIterator::Subdirectories)) + findUsingLs(current + '/' + entry, filter, found); + } + found->append(entry); + } +} + +// Used on 'ls' output on unix-like systems. +static void iterateLsOutput(const FilePath &base, + const QStringList &entries, + const FileFilter &filter, + const FilePath::IterateDirCallback &callBack) +{ + const QList<QRegularExpression> nameRegexps = + transform(filter.nameFilters, [](const QString &filter) { + QRegularExpression re; + re.setPattern(QRegularExpression::wildcardToRegularExpression(filter)); + QTC_CHECK(re.isValid()); + return re; + }); + + const auto nameMatches = [&nameRegexps](const QString &fileName) { + for (const QRegularExpression &re : nameRegexps) { + const QRegularExpressionMatch match = re.match(fileName); + if (match.hasMatch()) + return true; + } + return nameRegexps.isEmpty(); + }; + + // FIXME: Handle filters. For now bark on unsupported options. + QTC_CHECK(filter.fileFilters == QDir::NoFilter); + + for (const QString &entry : entries) { + if (!nameMatches(entry)) + continue; + const FilePath current = base.pathAppended(entry); + bool res = false; + if (callBack.index() == 0) + res = std::get<0>(callBack)(current); + else + res = std::get<1>(callBack)(current, current.filePathInfo()); + if (!res) + break; + } +} + +void UnixDeviceFileAccess::iterateDirectory( + const FilePath &filePath, + const FilePath::IterateDirCallback &callBack, + const FileFilter &filter) const +{ + // We try to use 'find' first, because that can filter better directly. + // Unfortunately, it's not installed on all devices by default. + if (m_tryUseFind) { + if (iterateWithFind(filePath, filter, callBack)) + return; + m_tryUseFind = false; // remember the failure for the next time and use the 'ls' fallback below. + } + + // if we do not have find - use ls as fallback + QStringList entries; + findUsingLs(filePath.path(), filter, &entries); + iterateLsOutput(filePath, entries, filter, callBack); +} + +} // Utils diff --git a/src/libs/utils/devicefileaccess.h b/src/libs/utils/devicefileaccess.h new file mode 100644 index 00000000000..e1ef5db1ddb --- /dev/null +++ b/src/libs/utils/devicefileaccess.h @@ -0,0 +1,194 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "utils_global.h" + +#include "fileutils.h" + +namespace Utils { + +// Base class including dummy implementation usable as fallback. +class QTCREATOR_UTILS_EXPORT DeviceFileAccess +{ +public: + virtual ~DeviceFileAccess(); + +protected: + friend class FilePath; + + virtual QString mapToDevicePath(const FilePath &filePath) const; + + virtual bool isExecutableFile(const FilePath &filePath) const; + virtual bool isReadableFile(const FilePath &filePath) const; + virtual bool isWritableFile(const FilePath &filePath) const; + virtual bool isReadableDirectory(const FilePath &filePath) const; + virtual bool isWritableDirectory(const FilePath &filePath) const; + virtual bool isFile(const FilePath &filePath) const; + virtual bool isDirectory(const FilePath &filePath) const; + virtual bool ensureWritableDirectory(const FilePath &filePath) const; + virtual bool ensureExistingFile(const FilePath &filePath) const; + virtual bool createDirectory(const FilePath &filePath) const; + virtual bool exists(const FilePath &filePath) const; + virtual bool removeFile(const FilePath &filePath) const; + virtual bool removeRecursively(const FilePath &filePath, QString *error) const; + virtual bool copyFile(const FilePath &filePath, const FilePath &target) const; + virtual bool renameFile(const FilePath &filePath, const FilePath &target) const; + + virtual OsType osType(const FilePath &filePath) const; + virtual FilePath symLinkTarget(const FilePath &filePath) const; + virtual FilePathInfo filePathInfo(const FilePath &filePath) const; + virtual QDateTime lastModified(const FilePath &filePath) const; + virtual QFile::Permissions permissions(const FilePath &filePath) const; + virtual bool setPermissions(const FilePath &filePath, QFile::Permissions) const; + virtual qint64 fileSize(const FilePath &filePath) const; + virtual qint64 bytesAvailable(const FilePath &filePath) const; + + virtual void iterateDirectory( + const FilePath &filePath, + const FilePath::IterateDirCallback &callBack, + const FileFilter &filter) const; + + virtual std::optional<QByteArray> fileContents( + const FilePath &filePath, + qint64 limit, + qint64 offset) const; + virtual bool writeFileContents( + const FilePath &filePath, + const QByteArray &data, + qint64 offset) const; + + virtual void asyncFileContents( + const FilePath &filePath, + const Continuation<std::optional<QByteArray>> &cont, + qint64 limit, + qint64 offset) const; + + virtual void asyncWriteFileContents( + const FilePath &filePath, + const Continuation<bool> &cont, + const QByteArray &data, + qint64 offset) const; + + virtual void asyncCopyFile( + const FilePath &filePath, + const Continuation<bool> &cont, + const FilePath &target) const; +}; + +class QTCREATOR_UTILS_EXPORT DesktopDeviceFileAccess : public DeviceFileAccess +{ +public: + ~DesktopDeviceFileAccess() override; + + static DesktopDeviceFileAccess *instance(); + +protected: + bool isExecutableFile(const FilePath &filePath) const override; + bool isReadableFile(const FilePath &filePath) const override; + bool isWritableFile(const FilePath &filePath) const override; + bool isReadableDirectory(const FilePath &filePath) const override; + bool isWritableDirectory(const FilePath &filePath) const override; + bool isFile(const FilePath &filePath) const override; + bool isDirectory(const FilePath &filePath) const override; + bool ensureWritableDirectory(const FilePath &filePath) const override; + bool ensureExistingFile(const FilePath &filePath) const override; + bool createDirectory(const FilePath &filePath) const override; + bool exists(const FilePath &filePath) const override; + bool removeFile(const FilePath &filePath) const override; + bool removeRecursively(const FilePath &filePath, QString *error) const override; + bool copyFile(const FilePath &filePath, const FilePath &target) const override; + bool renameFile(const FilePath &filePath, const FilePath &target) const override; + + OsType osType(const FilePath &filePath) const override; + FilePath symLinkTarget(const FilePath &filePath) const override; + FilePathInfo filePathInfo(const FilePath &filePath) const override; + QDateTime lastModified(const FilePath &filePath) const override; + QFile::Permissions permissions(const FilePath &filePath) const override; + bool setPermissions(const FilePath &filePath, QFile::Permissions) const override; + qint64 fileSize(const FilePath &filePath) const override; + qint64 bytesAvailable(const FilePath &filePath) const override; + + void iterateDirectory( + const FilePath &filePath, + const FilePath::IterateDirCallback &callBack, + const FileFilter &filter) const override; + + std::optional<QByteArray> fileContents( + const FilePath &filePath, + qint64 limit, + qint64 offset) const override; + bool writeFileContents( + const FilePath &filePath, + const QByteArray &data, + qint64 offset) const override; +}; + +class QTCREATOR_UTILS_EXPORT UnixDeviceFileAccess : public DeviceFileAccess +{ +public: + ~UnixDeviceFileAccess() override; + +protected: + virtual RunResult runInShell( + const QString &executable, + const QStringList &args, + const QByteArray &inputData = {}) const = 0; + bool runInShellSuccess( + const QString &executable, + const QStringList &args, + const QByteArray &stdInData = {}) const; + + bool isExecutableFile(const FilePath &filePath) const override; + bool isReadableFile(const FilePath &filePath) const override; + bool isWritableFile(const FilePath &filePath) const override; + bool isReadableDirectory(const FilePath &filePath) const override; + bool isWritableDirectory(const FilePath &filePath) const override; + bool isFile(const FilePath &filePath) const override; + bool isDirectory(const FilePath &filePath) const override; + bool ensureExistingFile(const FilePath &filePath) const override; + bool createDirectory(const FilePath &filePath) const override; + bool exists(const FilePath &filePath) const override; + bool removeFile(const FilePath &filePath) const override; + bool removeRecursively(const FilePath &filePath, QString *error) const override; + bool copyFile(const FilePath &filePath, const FilePath &target) const override; + bool renameFile(const FilePath &filePath, const FilePath &target) const override; + + FilePathInfo filePathInfo(const FilePath &filePath) const override; + OsType osType(const FilePath &filePath) const override; + FilePath symLinkTarget(const FilePath &filePath) const override; + QDateTime lastModified(const FilePath &filePath) const override; + QFile::Permissions permissions(const FilePath &filePath) const override; + bool setPermissions(const FilePath &filePath, QFile::Permissions) const override; + qint64 fileSize(const FilePath &filePath) const override; + qint64 bytesAvailable(const FilePath &filePath) const override; + + void iterateDirectory( + const FilePath &filePath, + const FilePath::IterateDirCallback &callBack, + const FileFilter &filter) const override; + + std::optional<QByteArray> fileContents( + const FilePath &filePath, + qint64 limit, + qint64 offset) const override; + bool writeFileContents( + const FilePath &filePath, + const QByteArray &data, + qint64 offset) const override; + +private: + bool iterateWithFind( + const FilePath &filePath, + const FileFilter &filter, + const FilePath::IterateDirCallback &callBack) const; + void findUsingLs( + const QString ¤t, + const FileFilter &filter, + QStringList *found) const; + + mutable bool m_tryUseFind = true; +}; + +} // Utils diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index 91796a6850f..645ded358e5 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -4,6 +4,7 @@ #include "filepath.h" #include "algorithm.h" +#include "devicefileaccess.h" #include "environment.h" #include "fileutils.h" #include "hostosinfo.h" @@ -15,7 +16,6 @@ #include <QDirIterator> #include <QFileInfo> #include <QRegularExpression> -#include <QStorageInfo> #include <QUrl> #include <QStringView> @@ -30,7 +30,6 @@ namespace Utils { static DeviceFileHooks s_deviceHooks; -static bool removeRecursivelyLocal(const FilePath &filePath, QString *error); inline bool isWindowsDriveLetter(QChar ch); @@ -359,136 +358,68 @@ void FilePath::setParts(const QStringView scheme, const QStringView host, const /// FilePath exists. bool FilePath::exists() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.exists, return false); - return s_deviceHooks.exists(*this); - } - return !isEmpty() && QFileInfo::exists(path()); + return fileAccess()->exists(*this); } /// \returns a bool indicating whether a path is writable. bool FilePath::isWritableDir() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.isWritableDir, return false); - return s_deviceHooks.isWritableDir(*this); - } - const QFileInfo fi{path()}; - return exists() && fi.isDir() && fi.isWritable(); + return fileAccess()->isWritableDirectory(*this); } bool FilePath::isWritableFile() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.isWritableFile, return false); - return s_deviceHooks.isWritableFile(*this); - } - const QFileInfo fi{path()}; - return fi.isWritable() && !fi.isDir(); + return fileAccess()->isWritableFile(*this); } bool FilePath::ensureWritableDir() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.ensureWritableDir, return false); - return s_deviceHooks.ensureWritableDir(*this); - } - const QFileInfo fi{path()}; - if (fi.isDir() && fi.isWritable()) - return true; - return QDir().mkpath(path()); + return fileAccess()->ensureWritableDirectory(*this); } bool FilePath::ensureExistingFile() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.ensureExistingFile, return false); - return s_deviceHooks.ensureExistingFile(*this); - } - QFile f(path()); - if (f.exists()) - return true; - f.open(QFile::WriteOnly); - f.close(); - return f.exists(); + return fileAccess()->ensureExistingFile(*this); } bool FilePath::isExecutableFile() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.isExecutableFile, return false); - return s_deviceHooks.isExecutableFile(*this); - } - const QFileInfo fi{path()}; - return fi.isExecutable() && !fi.isDir(); + return fileAccess()->isExecutableFile(*this); } bool FilePath::isReadableFile() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.isReadableFile, return false); - return s_deviceHooks.isReadableFile(*this); - } - const QFileInfo fi{path()}; - return fi.isReadable() && !fi.isDir(); + return fileAccess()->isReadableFile(*this); } bool FilePath::isReadableDir() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.isReadableDir, return false); - return s_deviceHooks.isReadableDir(*this); - } - const QFileInfo fi{path()}; - return fi.isReadable() && fi.isDir(); + return fileAccess()->isReadableDirectory(*this); } bool FilePath::isFile() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.isFile, return false); - return s_deviceHooks.isFile(*this); - } - const QFileInfo fi{path()}; - return fi.isFile(); + return fileAccess()->isFile(*this); } bool FilePath::isDir() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.isDir, return false); - return s_deviceHooks.isDir(*this); - } - const QFileInfo fi{path()}; - return fi.isDir(); + return fileAccess()->isDirectory(*this); } bool FilePath::createDir() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.createDir, return false); - return s_deviceHooks.createDir(*this); - } - QDir dir(path()); - return dir.mkpath(dir.absolutePath()); + return fileAccess()->createDirectory(*this); } FilePaths FilePath::dirEntries(const FileFilter &filter, QDir::SortFlags sort) const { FilePaths result; - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.iterateDirectory, return {}); - const auto callBack = [&result](const FilePath &path) { result.append(path); return true; }; - s_deviceHooks.iterateDirectory(*this, callBack, filter); - } else { - QDirIterator dit(path(), filter.nameFilters, filter.fileFilters, filter.iteratorFlags); - while (dit.hasNext()) - result.append(FilePath::fromString(dit.next())); - } + const auto callBack = [&result](const FilePath &path) { result.append(path); return true; }; + iterateDirectory(callBack, filter); // FIXME: Not all flags supported here. - const QDir::SortFlags sortBy = (sort & QDir::SortByMask); if (sortBy == QDir::Name) { Utils::sort(result); @@ -515,23 +446,7 @@ FilePaths FilePath::dirEntries(QDir::Filters filters) const void FilePath::iterateDirectory(const IterateDirCallback &callBack, const FileFilter &filter) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.iterateDirectory, return); - s_deviceHooks.iterateDirectory(*this, callBack, filter); - return; - } - - QDirIterator it(path(), filter.nameFilters, filter.fileFilters, filter.iteratorFlags); - while (it.hasNext()) { - const FilePath path = FilePath::fromString(it.next()); - bool res = false; - if (callBack.index() == 0) - res = std::get<0>(callBack)(path); - else - res = std::get<1>(callBack)(path, path.filePathInfo()); - if (!res) - return; - } + fileAccess()->iterateDirectory(*this, callBack, filter); } void FilePath::iterateDirectories(const FilePaths &dirs, @@ -544,26 +459,7 @@ void FilePath::iterateDirectories(const FilePaths &dirs, std::optional<QByteArray> FilePath::fileContents(qint64 maxSize, qint64 offset) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.fileContents, return {}); - return s_deviceHooks.fileContents(*this, maxSize, offset); - } - - const QString path = toString(); - QFile f(path); - if (!f.exists()) - return {}; - - if (!f.open(QFile::ReadOnly)) - return {}; - - if (offset != 0) - f.seek(offset); - - if (maxSize != -1) - return f.read(maxSize); - - return f.readAll(); + return fileAccess()->fileContents(*this, maxSize, offset); } bool FilePath::ensureReachable(const FilePath &other) const @@ -577,78 +473,30 @@ bool FilePath::ensureReachable(const FilePath &other) const return false; } - -void FilePath::asyncFileContents(const Continuation<const std::optional<QByteArray> &> &cont, - qint64 maxSize, - qint64 offset) const +void FilePath::asyncFileContents( + const Continuation<const std::optional<QByteArray> &> &cont, + qint64 maxSize, + qint64 offset) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.asyncFileContents, return); - s_deviceHooks.asyncFileContents(cont, *this, maxSize, offset); - return; - } - - cont(fileContents(maxSize, offset)); + return fileAccess()->asyncFileContents(*this, cont, maxSize, offset); } bool FilePath::writeFileContents(const QByteArray &data, qint64 offset) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.writeFileContents, return {}); - return s_deviceHooks.writeFileContents(*this, data, offset); - } - - QFile file(path()); - QTC_ASSERT(file.open(QFile::WriteOnly | QFile::Truncate), return false); - if (offset != 0) - file.seek(offset); - qint64 res = file.write(data); - return res == data.size(); + return fileAccess()->writeFileContents(*this, data, offset); } FilePathInfo FilePath::filePathInfo() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.filePathInfo, return {}); - return s_deviceHooks.filePathInfo(*this); - } - - FilePathInfo result; - - QFileInfo fi(path()); - result.fileSize = fi.size(); - result.lastModified = fi.lastModified(); - result.fileFlags = (FilePathInfo::FileFlag) int(fi.permissions()); - - if (fi.isDir()) - result.fileFlags |= FilePathInfo::DirectoryType; - if (fi.isFile()) - result.fileFlags |= FilePathInfo::FileType; - if (fi.exists()) - result.fileFlags |= FilePathInfo::ExistsFlag; - if (fi.isSymbolicLink()) - result.fileFlags |= FilePathInfo::LinkType; - if (fi.isBundle()) - result.fileFlags |= FilePathInfo::BundleType; - if (fi.isHidden()) - result.fileFlags |= FilePathInfo::HiddenFlag; - if (fi.isRoot()) - result.fileFlags |= FilePathInfo::RootFlag; - - return result; + return fileAccess()->filePathInfo(*this); } -void FilePath::asyncWriteFileContents(const Continuation<bool> &cont, - const QByteArray &data, - qint64 offset) const +void FilePath::asyncWriteFileContents( + const Continuation<bool> &cont, + const QByteArray &data, + qint64 offset) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.asyncWriteFileContents, return); - s_deviceHooks.asyncWriteFileContents(cont, *this, data, offset); - return; - } - - cont(writeFileContents(data, offset)); + return fileAccess()->asyncWriteFileContents(*this, cont, data, offset); } bool FilePath::needsDevice() const @@ -670,23 +518,12 @@ bool FilePath::isSameDevice(const FilePath &other) const /// \returns an empty FilePath if this is not a symbolic linl FilePath FilePath::symLinkTarget() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.symLinkTarget, return {}); - return s_deviceHooks.symLinkTarget(*this); - } - const QFileInfo info(path()); - if (!info.isSymLink()) - return {}; - return FilePath::fromString(info.symLinkTarget()); + return fileAccess()->symLinkTarget(*this); } QString FilePath::mapToDevicePath() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.mapToDevicePath, return path()); - return s_deviceHooks.mapToDevicePath(*this); - } - return path(); + return fileAccess()->mapToDevicePath(*this); } FilePath FilePath::withExecutableSuffix() const @@ -937,6 +774,23 @@ void FilePath::setFromString(const QString &unnormalizedFileName) setParts({}, {}, fileName); } +DeviceFileAccess *FilePath::fileAccess() const +{ + if (!needsDevice()) + return DesktopDeviceFileAccess::instance(); + + if (!s_deviceHooks.fileAccess) { + // Happens during startup and in tst_fsengine + QTC_CHECK(false); + return DesktopDeviceFileAccess::instance(); + } + + static DeviceFileAccess dummy; + DeviceFileAccess *access = s_deviceHooks.fileAccess(*this); + QTC_ASSERT(access, return &dummy); + return access; +} + /// Constructs a FilePath from \a filePath. The \a defaultExtension is appended /// to \a filename if that does not have an extension already. /// \a filePath is not checked for validity. @@ -1241,10 +1095,10 @@ FilePath FilePath::withNewPath(const QString &newPath) const */ FilePath FilePath::searchInDirectories(const FilePaths &dirs) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.searchInPath, return {}); - return s_deviceHooks.searchInPath(*this, dirs); - } + if (isAbsolutePath()) + return *this; + // FIXME: Ramp down use. + QTC_ASSERT(!needsDevice(), return {}); return Environment::systemEnvironment().searchInDirectories(path(), dirs); } @@ -1252,6 +1106,7 @@ FilePath FilePath::searchInPath(const FilePaths &additionalDirs, PathAmending am { if (isAbsolutePath()) return *this; + // FIXME: Ramp down use. FilePaths directories = deviceEnvironment().path(); if (!additionalDirs.isEmpty()) { if (amending == AppendToPath) @@ -1259,7 +1114,8 @@ FilePath FilePath::searchInPath(const FilePaths &additionalDirs, PathAmending am else directories = additionalDirs + directories; } - return searchInDirectories(directories); + QTC_ASSERT(!needsDevice(), return {}); + return Environment::systemEnvironment().searchInDirectories(path(), directories); } Environment FilePath::deviceEnvironment() const @@ -1338,47 +1194,27 @@ size_t FilePath::hash(uint seed) const QDateTime FilePath::lastModified() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.lastModified, return {}); - return s_deviceHooks.lastModified(*this); - } - return toFileInfo().lastModified(); + return fileAccess()->lastModified(*this); } QFile::Permissions FilePath::permissions() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.permissions, return {}); - return s_deviceHooks.permissions(*this); - } - return toFileInfo().permissions(); + return fileAccess()->permissions(*this); } bool FilePath::setPermissions(QFile::Permissions permissions) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.setPermissions, return false); - return s_deviceHooks.setPermissions(*this, permissions); - } - return QFile(path()).setPermissions(permissions); + return fileAccess()->setPermissions(*this, permissions); } OsType FilePath::osType() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.osType, return OsType::OsTypeLinux); - return s_deviceHooks.osType(*this); - } - return HostOsInfo::hostOs(); + return fileAccess()->osType(*this); } bool FilePath::removeFile() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.removeFile, return false); - return s_deviceHooks.removeFile(*this); - } - return QFile::remove(path()); + return fileAccess()->removeFile(*this); } /*! @@ -1390,11 +1226,7 @@ bool FilePath::removeFile() const */ bool FilePath::removeRecursively(QString *error) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.removeRecursively, return false); - return s_deviceHooks.removeRecursively(*this); - } - return removeRecursivelyLocal(*this, error); + return fileAccess()->removeRecursively(*this, error); } bool FilePath::copyFile(const FilePath &target) const @@ -1406,11 +1238,7 @@ bool FilePath::copyFile(const FilePath &target) const return false; return target.writeFileContents(*ba); } - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.copyFile, return false); - return s_deviceHooks.copyFile(*this, target); - } - return QFile::copy(path(), target.path()); + return fileAccess()->copyFile(*this, target); } void FilePath::asyncCopyFile(const std::function<void(bool)> &cont, const FilePath &target) const @@ -1420,90 +1248,24 @@ void FilePath::asyncCopyFile(const std::function<void(bool)> &cont, const FilePa if (ba) target.asyncWriteFileContents(cont, *ba); }); - } else if (needsDevice()) { - s_deviceHooks.asyncCopyFile(cont, *this, target); - } else { - cont(copyFile(target)); + return; } + return fileAccess()->asyncCopyFile(*this, cont, target); } bool FilePath::renameFile(const FilePath &target) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.renameFile, return false); - return s_deviceHooks.renameFile(*this, target); - } - return QFile::rename(path(), target.path()); + return fileAccess()->renameFile(*this, target); } qint64 FilePath::fileSize() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.fileSize, return false); - return s_deviceHooks.fileSize(*this); - } - return QFileInfo(path()).size(); + return fileAccess()->fileSize(*this); } qint64 FilePath::bytesAvailable() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.bytesAvailable, return false); - return s_deviceHooks.bytesAvailable(*this); - } - return QStorageInfo(path()).bytesAvailable(); -} - -static bool removeRecursivelyLocal(const FilePath &filePath, QString *error) -{ - QTC_ASSERT(!filePath.needsDevice(), return false); - QFileInfo fileInfo = filePath.toFileInfo(); - if (!fileInfo.exists() && !fileInfo.isSymLink()) - return true; - - QFile::setPermissions(fileInfo.absoluteFilePath(), fileInfo.permissions() | QFile::WriteUser); - - if (fileInfo.isDir()) { - QDir dir(fileInfo.absoluteFilePath()); - dir.setPath(dir.canonicalPath()); - if (dir.isRoot()) { - if (error) { - *error = QCoreApplication::translate("Utils::FileUtils", - "Refusing to remove root directory."); - } - return false; - } - if (dir.path() == QDir::home().canonicalPath()) { - if (error) { - *error = QCoreApplication::translate("Utils::FileUtils", - "Refusing to remove your home directory."); - } - return false; - } - - const QStringList fileNames = dir.entryList( - QDir::Files | QDir::Hidden | QDir::System | QDir::Dirs | QDir::NoDotAndDotDot); - for (const QString &fileName : fileNames) { - if (!removeRecursivelyLocal(filePath / fileName, error)) - return false; - } - if (!QDir::root().rmdir(dir.path())) { - if (error) { - *error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove directory \"%1\".") - .arg(filePath.toUserOutput()); - } - return false; - } - } else { - if (!QFile::remove(filePath.toString())) { - if (error) { - *error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove file \"%1\".") - .arg(filePath.toUserOutput()); - } - return false; - } - } - return true; + return fileAccess()->bytesAvailable(*this); } /*! diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h index 01100dc1320..0cc9455eef7 100644 --- a/src/libs/utils/filepath.h +++ b/src/libs/utils/filepath.h @@ -28,9 +28,12 @@ class tst_fileutils; // This becomes a friend of Utils::FilePath for testing pri namespace Utils { +class DeviceFileAccess; class Environment; class EnvironmentChange; +template <class ...Args> using Continuation = std::function<void(Args...)>; + class QTCREATOR_UTILS_EXPORT FileFilter { public: @@ -188,7 +191,6 @@ public: static void sort(FilePaths &files); // Asynchronous interface - template <class ...Args> using Continuation = std::function<void(Args...)>; void asyncCopyFile(const Continuation<bool> &cont, const FilePath &target) const; void asyncFileContents(const Continuation<const std::optional<QByteArray> &> &cont, qint64 maxSize = -1, @@ -232,6 +234,7 @@ private: static QString calcRelativePath(const QString &absolutePath, const QString &absoluteAnchorPath); void setPath(QStringView path); void setFromString(const QString &filepath); + DeviceFileAccess *fileAccess() const; [[nodiscard]] QString mapToDevicePath() const; [[nodiscard]] QString encodedHost() const; @@ -252,50 +255,11 @@ class QTCREATOR_UTILS_EXPORT DeviceFileHooks public: static DeviceFileHooks &instance(); - std::function<bool(const FilePath &)> isExecutableFile; - std::function<bool(const FilePath &)> isReadableFile; - std::function<bool(const FilePath &)> isReadableDir; - std::function<bool(const FilePath &)> isWritableDir; - std::function<bool(const FilePath &)> isWritableFile; - std::function<bool(const FilePath &)> isFile; - std::function<bool(const FilePath &)> isDir; - std::function<bool(const FilePath &)> ensureWritableDir; - std::function<bool(const FilePath &)> ensureExistingFile; - std::function<bool(const FilePath &)> createDir; - std::function<bool(const FilePath &)> exists; - std::function<bool(const FilePath &)> removeFile; - std::function<bool(const FilePath &)> removeRecursively; - std::function<bool(const FilePath &, const FilePath &)> copyFile; - std::function<bool(const FilePath &, const FilePath &)> renameFile; - std::function<FilePath(const FilePath &, const FilePaths &)> searchInPath; - std::function<FilePath(const FilePath &)> symLinkTarget; - std::function<QString(const FilePath &)> mapToDevicePath; - std::function<void(const FilePath &, - const FilePath::IterateDirCallback &, // Abort on 'false' return. - const FileFilter &)> - iterateDirectory; - std::function<std::optional<QByteArray>(const FilePath &, qint64, qint64)> fileContents; - std::function<bool(const FilePath &, const QByteArray &, qint64)> writeFileContents; - std::function<QDateTime(const FilePath &)> lastModified; - std::function<QFile::Permissions(const FilePath &)> permissions; - std::function<bool(const FilePath &, QFile::Permissions)> setPermissions; - std::function<OsType(const FilePath &)> osType; - std::function<Environment(const FilePath &)> environment; - std::function<qint64(const FilePath &)> fileSize; - std::function<qint64(const FilePath &)> bytesAvailable; + std::function<DeviceFileAccess *(const FilePath &)> fileAccess; std::function<QString(const FilePath &)> deviceDisplayName; - std::function<bool(const FilePath &, const FilePath &)> isSameDevice; - std::function<FilePathInfo(const FilePath &)> filePathInfo; - - - template <class ...Args> using Continuation = std::function<void(Args...)>; - std::function<void(const Continuation<bool> &, const FilePath &, const FilePath &)> asyncCopyFile; - std::function<void( - const Continuation<const std::optional<QByteArray> &> &, const FilePath &, qint64, qint64)> - asyncFileContents; - std::function<void(const Continuation<bool> &, const FilePath &, const QByteArray &, qint64)> - asyncWriteFileContents; std::function<bool(const FilePath &, const FilePath &)> ensureReachable; + std::function<Environment(const FilePath &)> environment; + std::function<bool(const FilePath &left, const FilePath &right)> isSameDevice; }; } // namespace Utils diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index 42d8f553894..989f6a3fcc9 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -646,46 +646,6 @@ FilePaths FileUtils::getOpenFilePaths(QWidget *parent, #endif // QT_WIDGETS_LIB -// Used on 'ls' output on unix-like systems. -static void iterateLsOutput(const FilePath &base, - const QStringList &entries, - const FileFilter &filter, - const FilePath::IterateDirCallback &callBack) -{ - const QList<QRegularExpression> nameRegexps = - transform(filter.nameFilters, [](const QString &filter) { - QRegularExpression re; - re.setPattern(QRegularExpression::wildcardToRegularExpression(filter)); - QTC_CHECK(re.isValid()); - return re; - }); - - const auto nameMatches = [&nameRegexps](const QString &fileName) { - for (const QRegularExpression &re : nameRegexps) { - const QRegularExpressionMatch match = re.match(fileName); - if (match.hasMatch()) - return true; - } - return nameRegexps.isEmpty(); - }; - - // FIXME: Handle filters. For now bark on unsupported options. - QTC_CHECK(filter.fileFilters == QDir::NoFilter); - - for (const QString &entry : entries) { - if (!nameMatches(entry)) - continue; - const FilePath current = base.pathAppended(entry); - bool res = false; - if (callBack.index() == 0) - res = std::get<0>(callBack)(current); - else - res = std::get<1>(callBack)(current, current.filePathInfo()); - if (!res) - break; - } -} - FilePathInfo::FileFlags fileInfoFlagsfromStatRawModeHex(const QString &hexString) { bool ok = false; @@ -741,120 +701,6 @@ FilePathInfo FileUtils::filePathInfoFromTriple(const QString &infos) return {size, flags, dt}; } -static bool iterateWithFindHelper( - const FilePath &filePath, - const FileFilter &filter, - const std::function<RunResult(const CommandLine &)> &runInShell, - const std::function<bool(const QString &)> callBack, - const QString &extraArguments) -{ - QTC_CHECK(filePath.isAbsolutePath()); - const QStringList arguments = filter.asFindArguments(filePath.path()); - - CommandLine cmdLine{"find", arguments}; - if (!extraArguments.isEmpty()) - cmdLine.addArgs(extraArguments, CommandLine::Raw); - - const RunResult result = runInShell(cmdLine); - const QString out = QString::fromUtf8(result.stdOut); - if (result.exitCode != 0) { - // Find returns non-zero exit code for any error it encounters, even if it finds some files. - - if (!out.startsWith('"' + filePath.path())) { - if (!filePath.exists()) // File does not exist, so no files to find. - return true; - - // If the output does not start with the path we are searching in, find has failed. - // Possibly due to unknown options. - return false; - } - } - - QStringList entries = out.split("\n", Qt::SkipEmptyParts); - if (entries.isEmpty()) - return true; - - // Remove the first line, it is always the directory we are searching in. - // as long as we do not specify "mindepth > 0" - if (entries.size() > 0) - entries.pop_front(); - for (const QString &entry : entries) { - if (!callBack(entry)) - break; - } - - return true; -} - -// returns whether 'find' could be used. -static bool iterateWithFind( - const FilePath &filePath, - const FileFilter &filter, - const std::function<RunResult(const CommandLine &)> &runInShell, - const FilePath::IterateDirCallback &callBack) -{ - const auto toFilePath = [&filePath, &callBack](const QString &entry) { - if (callBack.index() == 0) - return std::get<0>(callBack)(filePath.withNewPath(entry)); - - const QString fileName = entry.mid(1, entry.lastIndexOf('\"') - 1); - const QString infos = entry.mid(fileName.length() + 3); - - const FilePathInfo fi = FileUtils::filePathInfoFromTriple(infos); - if (!fi.fileFlags) - return true; - - const FilePath fp = filePath.withNewPath(fileName); - return std::get<1>(callBack)(fp, fi); - }; - - // TODO: Using stat -L will always return the link target, not the link itself. - // We may wan't to add the information that it is a link at some point. - QString infoArgs; - if (callBack.index() == 1) - infoArgs = R"(-exec echo -n \"{}\"" " \; -exec stat -L -c "%f %Y %s" "{}" \;)"; - - return iterateWithFindHelper(filePath, filter, runInShell, toFilePath, infoArgs); -} - -static void findUsingLs(const QString ¤t, - const FileFilter &filter, - const std::function<RunResult(const CommandLine &)> &runInShell, - QStringList *found) -{ - const RunResult result = runInShell({"ls", {"-1", "-p", "--", current}}); - const QStringList entries = QString::fromUtf8(result.stdOut).split('\n', Qt::SkipEmptyParts); - for (QString entry : entries) { - const QChar last = entry.back(); - if (last == '/') { - entry.chop(1); - if (filter.iteratorFlags.testFlag(QDirIterator::Subdirectories)) - findUsingLs(current + '/' + entry, filter, runInShell, found); - } - found->append(entry); - } -} - -void FileUtils::iterateUnixDirectory(const FilePath &filePath, - const FileFilter &filter, - bool *useFind, - const std::function<RunResult (const CommandLine &)> &runInShell, - const FilePath::IterateDirCallback &callBack) -{ - // We try to use 'find' first, because that can filter better directly. - // Unfortunately, it's not installed on all devices by default. - if (useFind && *useFind) { - if (iterateWithFind(filePath, filter, runInShell, callBack)) - return; - *useFind = false; // remember the failure for the next time and use the 'ls' fallback below. - } - - // if we do not have find - use ls as fallback - QStringList entries; - findUsingLs(filePath.path(), filter, runInShell, &entries); - iterateLsOutput(filePath, entries, filter, callBack); -} - /*! Copies the directory specified by \a srcFilePath recursively to \a tgtFilePath. \a tgtFilePath will contain the target directory, which will be created. Example usage: diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index 9949a82d858..76ad91efbc8 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -86,13 +86,6 @@ public: static FilePaths toFilePathList(const QStringList &paths); - static void iterateUnixDirectory( - const FilePath &base, - const FileFilter &filter, - bool *useFind, - const std::function<RunResult(const CommandLine &)> &runInShell, - const FilePath::IterateDirCallback &callBack); - static qint64 bytesAvailableFromDFOutput(const QByteArray &dfOutput); static FilePathInfo filePathInfoFromTriple(const QString &infos); diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index b5da80bbc5f..4cc71591c35 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -83,6 +83,8 @@ Project { "detailsbutton.h", "detailswidget.cpp", "detailswidget.h", + "devicefileaccess.cpp", + "devicefileaccess.h", "deviceshell.cpp", "deviceshell.h", "differ.cpp", diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index bcab94bcb8b..be7124c995e 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -32,6 +32,7 @@ #include <utils/algorithm.h> #include <utils/basetreeview.h> +#include <utils/devicefileaccess.h> #include <utils/deviceshell.h> #include <utils/environment.h> #include <utils/fileutils.h> @@ -113,6 +114,25 @@ private: FilePath m_devicePath; }; +class DockerDeviceFileAccess : public UnixDeviceFileAccess +{ +public: + DockerDeviceFileAccess(DockerDevicePrivate *dev) + : m_dev(dev) + {} + + RunResult runInShell(const QString &executable, + const QStringList &arguments, + const QByteArray &stdInData) const override; + + std::optional<QByteArray> fileContents( + const FilePath &filePath, + qint64 limit, + qint64 offset) const override; + + DockerDevicePrivate *m_dev = nullptr; +}; + class DockerDevicePrivate : public QObject { public: @@ -129,8 +149,6 @@ public: return runInShell(cmd, stdInData).exitCode == 0; } - std::optional<QByteArray> fileContents(const FilePath &filePath, qint64 limit, qint64 offset); - void updateContainerAccess(); void changeMounts(QStringList newMounts); bool ensureReachable(const FilePath &other); @@ -177,8 +195,8 @@ public: QString m_container; Environment m_cachedEnviroment; - bool m_useFind = true; // prefer find over ls and hacks, but be able to use ls as fallback bool m_isShutdown = false; + DockerDeviceFileAccess m_fileAccess{this}; }; class DockerProcessImpl : public Utils::ProcessInterface @@ -337,9 +355,28 @@ Tasks DockerDevicePrivate::validateMounts() const return result; } +RunResult DockerDeviceFileAccess::runInShell( + const QString &executable, + const QStringList &arguments, + const QByteArray &stdInData) const +{ + QTC_ASSERT(m_dev, return {}); + return m_dev->runInShell({FilePath::fromString(executable), arguments}, stdInData); +} + +std::optional<QByteArray> DockerDeviceFileAccess::fileContents( + const FilePath &filePath, + qint64 limit, + qint64 offset) const +{ + m_dev->updateContainerAccess(); + return UnixDeviceFileAccess::fileContents(filePath, limit, offset); +} + DockerDevice::DockerDevice(DockerSettings *settings, const DockerDeviceData &data) : d(new DockerDevicePrivate(this, settings, data)) { + setFileAccess(&d->m_fileAccess); setDisplayType(Tr::tr("Docker")); setOsType(OsTypeOtherUnix); setDefaultDisplayName(Tr::tr("Docker Image")); @@ -778,165 +815,6 @@ bool DockerDevice::handlesFile(const FilePath &filePath) const return false; } -bool DockerDevice::isExecutableFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-x", path}}); -} - -bool DockerDevice::isReadableFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-r", path, "-a", "-f", path}}); -} - -bool DockerDevice::isWritableFile(const Utils::FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-w", path, "-a", "-f", path}}); -} - -bool DockerDevice::isReadableDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-r", path, "-a", "-d", path}}); -} - -bool DockerDevice::isWritableDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-w", path, "-a", "-d", path}}); -} - -bool DockerDevice::isFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-f", path}}); -} - -bool DockerDevice::isDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-d", path}}); -} - -bool DockerDevice::createDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"mkdir", {"-p", path}}); -} - -bool DockerDevice::exists(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-e", path}}); -} - -bool DockerDevice::ensureExistingFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"touch", {path}}); -} - -bool DockerDevice::removeFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return d->runInShellSuccess({"rm", {filePath.path()}}); -} - -bool DockerDevice::removeRecursively(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - QTC_ASSERT(filePath.path().startsWith('/'), return false); - - const QString path = filePath.cleanPath().path(); - // We are expecting this only to be called in a context of build directories or similar. - // Chicken out in some cases that _might_ be user code errors. - QTC_ASSERT(path.startsWith('/'), return false); - const int levelsNeeded = path.startsWith("/home/") ? 4 : 3; - QTC_ASSERT(path.count('/') >= levelsNeeded, return false); - - return d->runInShellSuccess({"rm", {"-rf", "--", path}}); -} - -bool DockerDevice::copyFile(const FilePath &filePath, const FilePath &target) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - QTC_ASSERT(handlesFile(target), return false); - return d->runInShellSuccess({"cp", {filePath.path(), target.path()}}); -} - -bool DockerDevice::renameFile(const FilePath &filePath, const FilePath &target) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - QTC_ASSERT(handlesFile(target), return false); - return d->runInShellSuccess({"mv", {filePath.path(), target.path()}}); -} - -QDateTime DockerDevice::lastModified(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - const RunResult result = d->runInShell({"stat", {"-L", "-c", "%Y", filePath.path()}}); - qint64 secs = result.stdOut.toLongLong(); - const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC); - return dt; -} - -FilePath DockerDevice::symLinkTarget(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - const RunResult result = d->runInShell({"readlink", {"-n", "-e", filePath.path()}}); - const QString out = QString::fromUtf8(result.stdOut); - return out.isEmpty() ? FilePath() : filePath.withNewPath(out); -} - -qint64 DockerDevice::fileSize(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return -1); - const RunResult result = d->runInShell({"stat", {"-L", "-c", "%s", filePath.path()}}); - return result.stdOut.toLongLong(); -} - -QFileDevice::Permissions DockerDevice::permissions(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - - const RunResult result = d->runInShell({"stat", {"-L", "-c", "%a", filePath.path()}}); - const uint bits = result.stdOut.toUInt(nullptr, 8); - QFileDevice::Permissions perm = {}; -#define BIT(n, p) if (bits & (1<<n)) perm |= QFileDevice::p - BIT(0, ExeOther); - BIT(1, WriteOther); - BIT(2, ReadOther); - BIT(3, ExeGroup); - BIT(4, WriteGroup); - BIT(5, ReadGroup); - BIT(6, ExeUser); - BIT(7, WriteUser); - BIT(8, ReadUser); -#undef BIT - return perm; -} - -bool DockerDevice::setPermissions(const FilePath &filePath, - QFileDevice::Permissions permissions) const -{ - Q_UNUSED(permissions) - QTC_ASSERT(handlesFile(filePath), return {}); - QTC_CHECK(false); // FIXME: Implement. - return false; -} - bool DockerDevice::ensureReachable(const FilePath &other) const { if (other.needsDevice()) @@ -947,43 +825,6 @@ bool DockerDevice::ensureReachable(const FilePath &other) const return d->ensureReachable(other.parentDir()); } -void DockerDevice::iterateDirectory(const FilePath &filePath, - const FilePath::IterateDirCallback &callBack, - const FileFilter &filter) const -{ - QTC_ASSERT(handlesFile(filePath), return); - auto runInShell = [this](const CommandLine &cmd) { return d->runInShell(cmd); }; - FileUtils::iterateUnixDirectory(filePath, filter, &d->m_useFind, runInShell, callBack); -} - -std::optional<QByteArray> DockerDevice::fileContents(const FilePath &filePath, - qint64 limit, - qint64 offset) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - return d->fileContents(filePath, limit, offset); -} - -bool DockerDevice::writeFileContents(const FilePath &filePath, - const QByteArray &data, - qint64 offset) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - CommandLine cmd({"dd", {"of=" + filePath.path()}}); - if (offset != 0) { - cmd.addArg("bs=1"); - cmd.addArg(QString("seek=%1").arg(offset)); - } - return d->runInShellSuccess(cmd, data); -} - -FilePathInfo DockerDevice::filePathInfo(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - const RunResult stat = d->runInShell({"stat", {"-L", "-c", "%f %Y %s", filePath.path()}}); - return FileUtils::filePathInfoFromTriple(QString::fromLatin1(stat.stdOut)); -} - Environment DockerDevice::systemEnvironment() const { return d->environment(); @@ -995,28 +836,6 @@ void DockerDevice::aboutToBeRemoved() const detector.undoAutoDetect(id().toString()); } -std::optional<QByteArray> DockerDevicePrivate::fileContents(const FilePath &filePath, - qint64 limit, - qint64 offset) -{ - updateContainerAccess(); - - QStringList args = {"if=" + filePath.path(), "status=none"}; - if (limit > 0 || offset > 0) { - const qint64 gcd = std::gcd(limit, offset); - args += {QString("bs=%1").arg(gcd), - QString("count=%1").arg(limit / gcd), - QString("seek=%1").arg(offset / gcd)}; - } - - const RunResult r = m_shell->runInShell({"dd", args}); - - if (r.exitCode != 0) - return {}; - - return r.stdOut; -} - void DockerDevicePrivate::fetchSystemEnviroment() { updateContainerAccess(); diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h index 1382dc7d7b6..625b45b46ff 100644 --- a/src/plugins/docker/dockerdevice.h +++ b/src/plugins/docker/dockerdevice.h @@ -84,37 +84,6 @@ public: Utils::FilePath rootPath() const override; bool handlesFile(const Utils::FilePath &filePath) const override; - bool isExecutableFile(const Utils::FilePath &filePath) const override; - bool isReadableFile(const Utils::FilePath &filePath) const override; - bool isWritableFile(const Utils::FilePath &filePath) const override; - bool isReadableDirectory(const Utils::FilePath &filePath) const override; - bool isWritableDirectory(const Utils::FilePath &filePath) const override; - bool isFile(const Utils::FilePath &filePath) const override; - bool isDirectory(const Utils::FilePath &filePath) const override; - bool createDirectory(const Utils::FilePath &filePath) const override; - bool exists(const Utils::FilePath &filePath) const override; - bool ensureExistingFile(const Utils::FilePath &filePath) const override; - bool removeFile(const Utils::FilePath &filePath) const override; - bool removeRecursively(const Utils::FilePath &filePath) const override; - bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; - bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; - Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const override; - void iterateDirectory( - const Utils::FilePath &filePath, - const Utils::FilePath::IterateDirCallback &callBack, - const Utils::FileFilter &filter) const override; - std::optional<QByteArray> fileContents(const Utils::FilePath &filePath, - qint64 limit, - qint64 offset) const override; - bool writeFileContents(const Utils::FilePath &filePath, - const QByteArray &data, - qint64 offset) const override; - Utils::FilePathInfo filePathInfo(const Utils::FilePath &filePath) const override; - QDateTime lastModified(const Utils::FilePath &filePath) const override; - qint64 fileSize(const Utils::FilePath &filePath) const override; - QFileDevice::Permissions permissions(const Utils::FilePath &filePath) const override; - bool setPermissions(const Utils::FilePath &filePath, - QFileDevice::Permissions permissions) const override; bool ensureReachable(const Utils::FilePath &other) const override; Utils::Environment systemEnvironment() const override; diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp index ee5dae12e09..e65e9892a18 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp @@ -11,6 +11,7 @@ #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/runcontrol.h> +#include <utils/devicefileaccess.h> #include <utils/environment.h> #include <utils/hostosinfo.h> #include <utils/portlist.h> @@ -27,6 +28,8 @@ namespace ProjectExplorer { DesktopDevice::DesktopDevice() { + setFileAccess(DesktopDeviceFileAccess::instance()); + setupId(IDevice::AutoDetected, DESKTOP_DEVICE_ID); setType(DESKTOP_DEVICE_TYPE); setDefaultDisplayName(tr("Local PC")); @@ -136,33 +139,6 @@ bool DesktopDevice::handlesFile(const FilePath &filePath) const return !filePath.needsDevice(); } -void DesktopDevice::iterateDirectory( - const FilePath &filePath, - const FilePath::IterateDirCallback &callBack, - const FileFilter &filter) const -{ - QTC_CHECK(!filePath.needsDevice()); - filePath.iterateDirectory(callBack, filter); -} - -qint64 DesktopDevice::fileSize(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return -1); - return filePath.fileSize(); -} - -QFile::Permissions DesktopDevice::permissions(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - return filePath.permissions(); -} - -bool DesktopDevice::setPermissions(const FilePath &filePath, QFile::Permissions permissions) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - return filePath.setPermissions(permissions); -} - FilePath DesktopDevice::mapToGlobalPath(const Utils::FilePath &pathOnDevice) const { QTC_CHECK(!pathOnDevice.needsDevice()); @@ -174,117 +150,4 @@ Environment DesktopDevice::systemEnvironment() const return Environment::systemEnvironment(); } -bool DesktopDevice::isExecutableFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.isExecutableFile(); -} - -bool DesktopDevice::isReadableFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.isReadableFile(); -} - -bool DesktopDevice::isWritableFile(const Utils::FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.isWritableFile(); -} - -bool DesktopDevice::isReadableDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.isReadableDir(); -} - -bool DesktopDevice::isWritableDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.isWritableDir(); -} - -bool DesktopDevice::isFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.isFile(); -} - -bool DesktopDevice::isDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.isDir(); -} - -bool DesktopDevice::createDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.createDir(); -} - -bool DesktopDevice::exists(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.exists(); -} - -bool DesktopDevice::ensureExistingFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.ensureExistingFile(); -} - -bool DesktopDevice::removeFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.removeFile(); -} - -bool DesktopDevice::removeRecursively(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.removeRecursively(); -} - -bool DesktopDevice::copyFile(const FilePath &filePath, const FilePath &target) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.copyFile(target); -} - -bool DesktopDevice::renameFile(const FilePath &filePath, const FilePath &target) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - QTC_ASSERT(handlesFile(target), return false); - return filePath.renameFile(target); -} - -QDateTime DesktopDevice::lastModified(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - return filePath.lastModified(); -} - -FilePath DesktopDevice::symLinkTarget(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - return filePath.symLinkTarget(); -} - -std::optional<QByteArray> DesktopDevice::fileContents(const FilePath &filePath, - qint64 limit, - qint64 offset) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - return filePath.fileContents(limit, offset); -} - -bool DesktopDevice::writeFileContents(const FilePath &filePath, - const QByteArray &data, - qint64 offset) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - return filePath.writeFileContents(data, offset); -} - } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.h b/src/plugins/projectexplorer/devicesupport/desktopdevice.h index d0d89f7458c..7144eba8eea 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.h +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.h @@ -34,35 +34,6 @@ public: bool handlesFile(const Utils::FilePath &filePath) const override; Utils::Environment systemEnvironment() const override; - bool isExecutableFile(const Utils::FilePath &filePath) const override; - bool isReadableFile(const Utils::FilePath &filePath) const override; - bool isWritableFile(const Utils::FilePath &filePath) const override; - bool isReadableDirectory(const Utils::FilePath &filePath) const override; - bool isWritableDirectory(const Utils::FilePath &filePath) const override; - bool isFile(const Utils::FilePath &filePath) const override; - bool isDirectory(const Utils::FilePath &filePath) const override; - bool ensureExistingFile(const Utils::FilePath &filePath) const override; - bool createDirectory(const Utils::FilePath &filePath) const override; - bool exists(const Utils::FilePath &filePath) const override; - bool removeFile(const Utils::FilePath &filePath) const override; - bool removeRecursively(const Utils::FilePath &filePath) const override; - bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; - bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; - QDateTime lastModified(const Utils::FilePath &filePath) const override; - Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const override; - void iterateDirectory( - const Utils::FilePath &filePath, - const Utils::FilePath::IterateDirCallback &callBack, - const Utils::FileFilter &filter) const override; - std::optional<QByteArray> fileContents(const Utils::FilePath &filePath, - qint64 limit, - qint64 offset) const override; - bool writeFileContents(const Utils::FilePath &filePath, - const QByteArray &data, - qint64 offset) const override; - qint64 fileSize(const Utils::FilePath &filePath) const override; - QFile::Permissions permissions(const Utils::FilePath &filePath) const override; - bool setPermissions(const Utils::FilePath &filePath, QFile::Permissions) const override; Utils::FilePath mapToGlobalPath(const Utils::FilePath &pathOnDevice) const override; protected: diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp index 8a98a11b292..2d7d060427b 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp @@ -9,7 +9,9 @@ #include <coreplugin/messagemanager.h> #include <projectexplorer/projectexplorerconstants.h> + #include <utils/algorithm.h> +#include <utils/devicefileaccess.h> #include <utils/environment.h> #include <utils/fileutils.h> #include <utils/fsengine/fsengine.h> @@ -405,30 +407,6 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager DeviceFileHooks &deviceHooks = DeviceFileHooks::instance(); - deviceHooks.isExecutableFile = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->isExecutableFile(filePath); - }; - - deviceHooks.isReadableFile = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->isReadableFile(filePath); - }; - - deviceHooks.isReadableDir = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->isReadableDirectory(filePath); - }; - - deviceHooks.isWritableDir = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->isWritableDirectory(filePath); - }; - deviceHooks.isSameDevice = [](const FilePath &left, const FilePath &right) { auto leftDevice = DeviceManager::deviceForPath(left); auto rightDevice = DeviceManager::deviceForPath(right); @@ -436,150 +414,12 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager return leftDevice == rightDevice; }; - deviceHooks.isWritableFile = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->isWritableFile(filePath); - }; - - deviceHooks.isFile = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->isFile(filePath); - }; - - deviceHooks.isDir = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->isDirectory(filePath); - }; - - deviceHooks.ensureWritableDir = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->ensureWritableDirectory(filePath); - }; - - deviceHooks.ensureExistingFile = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->ensureExistingFile(filePath); - }; - - deviceHooks.createDir = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->createDirectory(filePath); - }; - - deviceHooks.exists = [](const FilePath &filePath) { + deviceHooks.fileAccess = [](const FilePath &filePath) -> DeviceFileAccess * { + if (!filePath.needsDevice()) + return DesktopDeviceFileAccess::instance(); auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->exists(filePath); - }; - - deviceHooks.removeFile = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->removeFile(filePath); - }; - - deviceHooks.removeRecursively = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->removeRecursively(filePath); - }; - - deviceHooks.copyFile = [](const FilePath &filePath, const FilePath &target) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->copyFile(filePath, target); - }; - - deviceHooks.renameFile = [](const FilePath &filePath, const FilePath &target) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->renameFile(filePath, target); - }; - - deviceHooks.searchInPath = [](const FilePath &filePath, const FilePaths &dirs) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return FilePath{}); - return device->searchExecutable(filePath.path(), dirs); - }; - - deviceHooks.symLinkTarget = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return FilePath{}); - return device->symLinkTarget(filePath); - }; - - deviceHooks.mapToDevicePath = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return QString{}); - return device->mapToDevicePath(filePath); - }; - - deviceHooks.iterateDirectory = [](const FilePath &filePath, - const FilePath::IterateDirCallback &callBack, - const FileFilter &filter) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return ); - device->iterateDirectory(filePath, callBack, filter); - }; - - deviceHooks.fileContents = - [](const FilePath &filePath, qint64 maxSize, qint64 offset) -> std::optional<QByteArray> { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return {}); - return device->fileContents(filePath, maxSize, offset); - }; - - deviceHooks.asyncFileContents = [](const Continuation<std::optional<QByteArray>> &cont, - const FilePath &filePath, - qint64 maxSize, - qint64 offset) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return); - device->asyncFileContents(cont, filePath, maxSize, offset); - }; - - deviceHooks.writeFileContents = [](const FilePath &filePath, - const QByteArray &data, - qint64 offset) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->writeFileContents(filePath, data, offset); - }; - - deviceHooks.filePathInfo = [](const FilePath &filePath) -> FilePathInfo { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return {}); - return device->filePathInfo(filePath); - }; - - deviceHooks.lastModified = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return QDateTime()); - return device->lastModified(filePath); - }; - - deviceHooks.permissions = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return QFile::Permissions()); - return device->permissions(filePath); - }; - - deviceHooks.setPermissions = [](const FilePath &filePath, QFile::Permissions permissions) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->setPermissions(filePath, permissions); - }; - - deviceHooks.osType = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return OsTypeOther); - return device->osType(); + QTC_ASSERT(device, return nullptr); + return device->fileAccess(); }; deviceHooks.environment = [](const FilePath &filePath) { @@ -588,18 +428,6 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager return device->systemEnvironment(); }; - deviceHooks.fileSize = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return qint64(-1)); - return device->fileSize(filePath); - }; - - deviceHooks.bytesAvailable = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return qint64(-1)); - return device->bytesAvailable(filePath); - }; - deviceHooks.deviceDisplayName = [](const FilePath &filePath) { auto device = DeviceManager::deviceForPath(filePath); QTC_ASSERT(device, return QString()); diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index ed6dcc73824..110f3f93fd0 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -15,6 +15,7 @@ #include <projectexplorer/target.h> +#include <utils/devicefileaccess.h> #include <utils/displayname.h> #include <utils/icon.h> #include <utils/portlist.h> @@ -130,6 +131,7 @@ public: IDevice::DeviceState deviceState = IDevice::DeviceStateUnknown; IDevice::MachineType machineType = IDevice::Hardware; OsType osType = OsTypeOther; + DeviceFileAccess *fileAccess = nullptr; int version = 0; // This is used by devices that have been added by the SDK. QReadWriteLock lock; // Currently used to protect sshParameters only @@ -153,6 +155,8 @@ IDevice::IDevice() : d(new Internal::IDevicePrivate) { } +IDevice::~IDevice() = default; + void IDevice::setOpenTerminal(const IDevice::OpenTerminal &openTerminal) { d->openTerminal = openTerminal; @@ -191,6 +195,11 @@ bool IDevice::isAnyUnixDevice() const return d->osType == OsTypeLinux || d->osType == OsTypeMac || d->osType == OsTypeOtherUnix; } +DeviceFileAccess *IDevice::fileAccess() const +{ + return d->fileAccess; +} + FilePath IDevice::mapToGlobalPath(const FilePath &pathOnDevice) const { if (pathOnDevice.needsDevice()) { @@ -219,113 +228,6 @@ bool IDevice::handlesFile(const FilePath &filePath) const return false; } -bool IDevice::isExecutableFile(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::isReadableFile(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::isWritableFile(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::isReadableDirectory(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::isWritableDirectory(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::isFile(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::isDirectory(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::ensureWritableDirectory(const FilePath &filePath) const -{ - if (isWritableDirectory(filePath)) - return true; - return createDirectory(filePath); -} - -bool IDevice::ensureExistingFile(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::createDirectory(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::exists(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::removeFile(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::removeRecursively(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::copyFile(const FilePath &filePath, const FilePath &target) const -{ - Q_UNUSED(filePath); - Q_UNUSED(target); - QTC_CHECK(false); - return false; -} - -bool IDevice::renameFile(const FilePath &filePath, const FilePath &target) const -{ - Q_UNUSED(filePath); - Q_UNUSED(target); - QTC_CHECK(false); - return false; -} - FilePath IDevice::searchExecutableInPath(const QString &fileName) const { FilePaths paths; @@ -341,108 +243,13 @@ FilePath IDevice::searchExecutable(const QString &fileName, const FilePaths &dir dir = mapToGlobalPath(dir); QTC_CHECK(handlesFile(dir)); const FilePath candidate = dir / fileName; - if (isExecutableFile(candidate)) + if (candidate.isExecutableFile()) return candidate; } return {}; } -FilePath IDevice::symLinkTarget(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return {}; -} - -void IDevice::iterateDirectory(const FilePath &filePath, - const FilePath::IterateDirCallback &callBack, - const FileFilter &filter) const -{ - Q_UNUSED(filePath); - Q_UNUSED(callBack); - Q_UNUSED(filter); - QTC_CHECK(false); -} - -std::optional<QByteArray> IDevice::fileContents(const FilePath &filePath, - qint64 limit, - qint64 offset) const -{ - Q_UNUSED(filePath); - Q_UNUSED(limit); - Q_UNUSED(offset); - QTC_CHECK(false); - return {}; -} - -void IDevice::asyncFileContents(const Continuation<std::optional<QByteArray>> &cont, - const FilePath &filePath, - qint64 limit, - qint64 offset) const -{ - cont(fileContents(filePath, limit, offset)); -} - -bool IDevice::writeFileContents(const FilePath &filePath, const QByteArray &data, qint64 offset) const -{ - Q_UNUSED(filePath); - Q_UNUSED(data); - Q_UNUSED(offset); - QTC_CHECK(false); - return {}; -} - -FilePathInfo IDevice::filePathInfo(const Utils::FilePath &filePath) const -{ - bool exists = filePath.exists(); - if (!exists) - return {}; - - FilePathInfo result { - filePath.fileSize(), - {FilePathInfo::ExistsFlag}, - filePath.lastModified(), - }; - - if (filePath.isDir()) - result.fileFlags |= FilePathInfo::DirectoryType; - if (filePath.isFile()) - result.fileFlags |= FilePathInfo::FileType; - if (filePath.isRootPath()) - result.fileFlags |= FilePathInfo::RootFlag; - - return result; -} - -void IDevice::asyncWriteFileContents(const Continuation<bool> &cont, - const FilePath &filePath, - const QByteArray &data, - qint64 offset) const -{ - cont(writeFileContents(filePath, data, offset)); -} - -QDateTime IDevice::lastModified(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - return {}; -} - -QFileDevice::Permissions IDevice::permissions(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return {}; -} - -bool IDevice::setPermissions(const FilePath &filePath, QFile::Permissions) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - ProcessInterface *IDevice::createProcessInterface() const { QTC_CHECK(false); @@ -463,22 +270,6 @@ Environment IDevice::systemEnvironment() const return Environment::systemEnvironment(); } -qint64 IDevice::fileSize(const FilePath &filePath) const -{ - Q_UNUSED(filePath) - QTC_CHECK(false); - return -1; -} - -qint64 IDevice::bytesAvailable(const Utils::FilePath &filePath) const -{ - Q_UNUSED(filePath) - QTC_CHECK(false); - return -1; -} - -IDevice::~IDevice() = default; - /*! Specifies a free-text name for the device to be displayed in GUI elements. */ @@ -513,6 +304,11 @@ void IDevice::setOsType(OsType osType) d->osType = osType; } +void IDevice::setFileAccess(DeviceFileAccess *fileAccess) +{ + d->fileAccess = fileAccess; +} + IDevice::DeviceInfo IDevice::deviceInformation() const { const QString key = QCoreApplication::translate("ProjectExplorer::IDevice", "Device"); diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h index ac46ff77f47..0f5649afd6c 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.h +++ b/src/plugins/projectexplorer/devicesupport/idevice.h @@ -27,6 +27,7 @@ QT_END_NAMESPACE namespace Utils { class CommandLine; +class DeviceFileAccess; class Environment; class Icon; class PortList; @@ -214,62 +215,23 @@ public: bool isMacDevice() const { return osType() == Utils::OsTypeMac; } bool isAnyUnixDevice() const; + Utils::DeviceFileAccess *fileAccess() const; + virtual bool handlesFile(const Utils::FilePath &filePath) const; + virtual Utils::FilePath mapToGlobalPath(const Utils::FilePath &pathOnDevice) const; virtual QString mapToDevicePath(const Utils::FilePath &globalPath) const; - virtual bool handlesFile(const Utils::FilePath &filePath) const; - virtual bool isExecutableFile(const Utils::FilePath &filePath) const; - virtual bool isReadableFile(const Utils::FilePath &filePath) const; - virtual bool isWritableFile(const Utils::FilePath &filePath) const; - virtual bool isReadableDirectory(const Utils::FilePath &filePath) const; - virtual bool isWritableDirectory(const Utils::FilePath &filePath) const; - virtual bool isFile(const Utils::FilePath &filePath) const; - virtual bool isDirectory(const Utils::FilePath &filePath) const; - virtual bool ensureWritableDirectory(const Utils::FilePath &filePath) const; - virtual bool ensureExistingFile(const Utils::FilePath &filePath) const; - virtual bool createDirectory(const Utils::FilePath &filePath) const; - virtual bool exists(const Utils::FilePath &filePath) const; - virtual bool removeFile(const Utils::FilePath &filePath) const; - virtual bool removeRecursively(const Utils::FilePath &filePath) const; - virtual bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const; - virtual bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const; virtual Utils::FilePath searchExecutableInPath(const QString &fileName) const; virtual Utils::FilePath searchExecutable(const QString &fileName, const Utils::FilePaths &dirs) const; - virtual Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const; - virtual void iterateDirectory( - const Utils::FilePath &filePath, - const Utils::FilePath::IterateDirCallback &callBack, - const Utils::FileFilter &filter) const; - - virtual std::optional<QByteArray> fileContents(const Utils::FilePath &filePath, - qint64 limit, - qint64 offset) const; - virtual bool writeFileContents(const Utils::FilePath &filePath, - const QByteArray &data, - qint64 offset) const; - virtual Utils::FilePathInfo filePathInfo(const Utils::FilePath &filePath) const; - virtual QDateTime lastModified(const Utils::FilePath &filePath) const; - virtual QFile::Permissions permissions(const Utils::FilePath &filePath) const; - virtual bool setPermissions(const Utils::FilePath &filePath, QFile::Permissions) const; + virtual Utils::ProcessInterface *createProcessInterface() const; virtual FileTransferInterface *createFileTransferInterface( const FileTransferSetupData &setup) const; virtual Utils::Environment systemEnvironment() const; - virtual qint64 fileSize(const Utils::FilePath &filePath) const; - virtual qint64 bytesAvailable(const Utils::FilePath &filePath) const; virtual void aboutToBeRemoved() const {} - virtual void asyncFileContents(const Continuation<std::optional<QByteArray>> &cont, - const Utils::FilePath &filePath, - qint64 limit, - qint64 offset) const; - virtual void asyncWriteFileContents(const Continuation<bool> &cont, - const Utils::FilePath &filePath, - const QByteArray &data, - qint64 offset) const; - virtual bool ensureReachable(const Utils::FilePath &other) const; virtual bool prepareForBuild(const Target *target); @@ -284,6 +246,7 @@ protected: void setOpenTerminal(const OpenTerminal &openTerminal); void setDisplayType(const QString &type); void setOsType(Utils::OsType osType); + void setFileAccess(Utils::DeviceFileAccess *fileAccess); private: IDevice(const IDevice &) = delete; diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index dae5d277fd2..884412f5b5f 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -1118,8 +1118,7 @@ static FilePaths findCompilerCandidates(const ToolchainDetector &detector, return true; }; const FilePath globalDir = device->mapToGlobalPath(deviceDir); - device->iterateDirectory(globalDir, callBack, - {nameFilters, QDir::Files | QDir::Executable}); + globalDir.iterateDirectory(callBack, {nameFilters, QDir::Files | QDir::Executable}); } } else { // The normal, local host case. diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index f9018e04e78..b5b9205377d 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -25,6 +25,7 @@ #include <projectexplorer/runcontrol.h> #include <utils/algorithm.h> +#include <utils/devicefileaccess.h> #include <utils/deviceshell.h> #include <utils/environment.h> #include <utils/hostosinfo.h> @@ -349,6 +350,21 @@ private: // LinuxDevicePrivate class ShellThreadHandler; +class LinuxDevicePrivate; + +class LinuxDeviceFileAccess : public UnixDeviceFileAccess +{ +public: + LinuxDeviceFileAccess(LinuxDevicePrivate *dev) + : m_dev(dev) + {} + + RunResult runInShell(const QString &executable, + const QStringList &arguments, + const QByteArray &stdInData) const override; + + LinuxDevicePrivate *m_dev; +}; class LinuxDevicePrivate { @@ -358,9 +374,6 @@ public: bool setupShell(); RunResult runInShell(const CommandLine &cmd, const QByteArray &stdInData = {}); - bool runInShellSuccess(const CommandLine &cmd, const QByteArray &stdInData = {}) { - return runInShell(cmd, stdInData).exitCode == 0; - } void attachToSharedConnection(SshConnectionHandle *connectionHandle, const SshParameters &sshParameters); @@ -370,9 +383,16 @@ public: ShellThreadHandler *m_handler = nullptr; mutable QMutex m_shellMutex; QList<QtcProcess *> m_terminals; - bool m_useFind = true; + LinuxDeviceFileAccess m_fileAccess{this}; }; +RunResult LinuxDeviceFileAccess::runInShell(const QString &executable, + const QStringList &arguments, + const QByteArray &stdInData) const +{ + return m_dev->runInShell({FilePath::fromString(executable), arguments}, stdInData); +} + // SshProcessImpl class SshProcessInterfacePrivate : public QObject @@ -465,7 +485,7 @@ qint64 SshProcessInterface::processId() const bool SshProcessInterface::runInShell(const CommandLine &command, const QByteArray &data) { - return d->m_devicePrivate->runInShellSuccess(command, data); + return d->m_devicePrivate->runInShell(command, data).exitCode == 0; } void SshProcessInterface::start() @@ -904,6 +924,7 @@ private: LinuxDevice::LinuxDevice() : d(new LinuxDevicePrivate(this)) { + setFileAccess(&d->m_fileAccess); setDisplayType(Tr::tr("Remote Linux")); setDefaultDisplayName(Tr::tr("Remote Linux Device")); setOsType(OsTypeLinux); @@ -1121,223 +1142,6 @@ void LinuxDevicePrivate::attachToSharedConnection(SshConnectionHandle *connectio emit connectionHandle->connected(socketFilePath); } -bool LinuxDevice::isExecutableFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-x", path}}); -} - -bool LinuxDevice::isReadableFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-r", path, "-a", "-f", path}}); -} - -bool LinuxDevice::isWritableFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-w", path, "-a", "-f", path}}); -} - -bool LinuxDevice::isReadableDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-r", path, "-a", "-d", path}}); -} - -bool LinuxDevice::isWritableDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-w", path, "-a", "-d", path}}); -} - -bool LinuxDevice::isFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-f", path}}); -} - -bool LinuxDevice::isDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-d", path}}); -} - -bool LinuxDevice::createDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"mkdir", {"-p", path}}); -} - -bool LinuxDevice::exists(const FilePath &filePath) const -{ - DEBUG("filepath " << filePath.path()); - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-e", path}}); -} - -bool LinuxDevice::ensureExistingFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"touch", {path}}); -} - -bool LinuxDevice::removeFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return d->runInShellSuccess({"rm", {filePath.path()}}); -} - -bool LinuxDevice::removeRecursively(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - QTC_ASSERT(filePath.path().startsWith('/'), return false); - - const QString path = filePath.cleanPath().path(); - // We are expecting this only to be called in a context of build directories or similar. - // Chicken out in some cases that _might_ be user code errors. - QTC_ASSERT(path.startsWith('/'), return false); - const int levelsNeeded = path.startsWith("/home/") ? 3 : 2; - QTC_ASSERT(path.count('/') >= levelsNeeded, return false); - - return d->runInShellSuccess({"rm", {"-rf", "--", path}}); -} - -bool LinuxDevice::copyFile(const FilePath &filePath, const FilePath &target) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - QTC_ASSERT(handlesFile(target), return false); - return d->runInShellSuccess({"cp", {filePath.path(), target.path()}}); -} - -bool LinuxDevice::renameFile(const FilePath &filePath, const FilePath &target) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - QTC_ASSERT(handlesFile(target), return false); - return d->runInShellSuccess({"mv", {filePath.path(), target.path()}}); -} - -QDateTime LinuxDevice::lastModified(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - const RunResult result = d->runInShell({"stat", {"-L", "-c", "%Y", filePath.path()}}); - const qint64 secs = result.stdOut.toLongLong(); - const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC); - return dt; -} - -FilePath LinuxDevice::symLinkTarget(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - const RunResult result = d->runInShell({"readlink", {"-n", "-e", filePath.path()}}); - return result.stdOut.isEmpty() ? FilePath() - : filePath.withNewPath(QString::fromUtf8(result.stdOut)); -} - -qint64 LinuxDevice::fileSize(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return -1); - const RunResult result = d->runInShell({"stat", {"-L", "-c", "%s", filePath.path()}}); - return result.stdOut.toLongLong(); -} - -qint64 LinuxDevice::bytesAvailable(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return -1); - CommandLine cmd("df", {"-k"}); - cmd.addArg(filePath.path()); - const RunResult result = d->runInShell(cmd); - return FileUtils::bytesAvailableFromDFOutput(result.stdOut); -} - -QFileDevice::Permissions LinuxDevice::permissions(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - const RunResult result = d->runInShell({"stat", {"-L", "-c", "%a", filePath.path()}}); - const uint bits = result.stdOut.toUInt(nullptr, 8); - QFileDevice::Permissions perm = {}; -#define BIT(n, p) if (bits & (1<<n)) perm |= QFileDevice::p - BIT(0, ExeOther); - BIT(1, WriteOther); - BIT(2, ReadOther); - BIT(3, ExeGroup); - BIT(4, WriteGroup); - BIT(5, ReadGroup); - BIT(6, ExeUser); - BIT(7, WriteUser); - BIT(8, ReadUser); -#undef BIT - return perm; -} - -bool LinuxDevice::setPermissions(const FilePath &filePath, QFileDevice::Permissions permissions) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const int flags = int(permissions); - return d->runInShellSuccess({"chmod", {QString::number(flags, 16), filePath.path()}}); -} - -void LinuxDevice::iterateDirectory(const FilePath &filePath, - const FilePath::IterateDirCallback &callBack, - const FileFilter &filter) const -{ - QTC_ASSERT(handlesFile(filePath), return); - auto runInShell = [this](const CommandLine &cmd) { return d->runInShell(cmd); }; - FileUtils::iterateUnixDirectory(filePath, filter, &d->m_useFind, runInShell, callBack); -} - -FilePathInfo LinuxDevice::filePathInfo(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - const RunResult stat = d->runInShell({"stat", {"-L", "-c", "%f %Y %s", filePath.path()}}); - return FileUtils::filePathInfoFromTriple(QString::fromLatin1(stat.stdOut)); -} - -std::optional<QByteArray> LinuxDevice::fileContents(const FilePath &filePath, - qint64 limit, - qint64 offset) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - QString args = "if=" + filePath.path() + " status=none"; - if (limit > 0 || offset > 0) { - const qint64 gcd = std::gcd(limit, offset); - args += QString(" bs=%1 count=%2 seek=%3").arg(gcd).arg(limit / gcd).arg(offset / gcd); - } - CommandLine cmd(FilePath::fromString("dd"), args, CommandLine::Raw); - - const RunResult result = d->runInShell(cmd); - if (result.exitCode != 0) { - DEBUG("fileContents failed"); - return {}; - } - - DEBUG(result.stdOut << QByteArray::fromHex(result.stdOut)); - return result.stdOut; -} - -bool LinuxDevice::writeFileContents(const FilePath &filePath, - const QByteArray &data, - qint64 offset) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - CommandLine cmd({"dd", {"of=" + filePath.path()}}); - if (offset != 0) { - cmd.addArg("bs=1"); - cmd.addArg(QString("seek=%1").arg(offset)); - } - return d->runInShellSuccess(cmd, data); -} - static FilePaths dirsToCreate(const FilesToTransfer &files) { FilePaths dirs; diff --git a/src/plugins/remotelinux/linuxdevice.h b/src/plugins/remotelinux/linuxdevice.h index b1e992c024a..51517ed8622 100644 --- a/src/plugins/remotelinux/linuxdevice.h +++ b/src/plugins/remotelinux/linuxdevice.h @@ -37,40 +37,11 @@ public: Utils::FilePath rootPath() const override; bool handlesFile(const Utils::FilePath &filePath) const override; - bool isExecutableFile(const Utils::FilePath &filePath) const override; - bool isReadableFile(const Utils::FilePath &filePath) const override; - bool isWritableFile(const Utils::FilePath &filePath) const override; - bool isReadableDirectory(const Utils::FilePath &filePath) const override; - bool isWritableDirectory(const Utils::FilePath &filePath) const override; - bool isFile(const Utils::FilePath &filePath) const override; - bool isDirectory(const Utils::FilePath &filePath) const override; - bool createDirectory(const Utils::FilePath &filePath) const override; - bool exists(const Utils::FilePath &filePath) const override; - bool ensureExistingFile(const Utils::FilePath &filePath) const override; - bool removeFile(const Utils::FilePath &filePath) const override; - bool removeRecursively(const Utils::FilePath &filePath) const override; - bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; - bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; - Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const override; - void iterateDirectory(const Utils::FilePath &filePath, - const Utils::FilePath::IterateDirCallback &callBack, - const Utils::FileFilter &filter) const override; - std::optional<QByteArray> fileContents(const Utils::FilePath &filePath, - qint64 limit, - qint64 offset) const override; - bool writeFileContents(const Utils::FilePath &filePath, - const QByteArray &data, - qint64 offset) const override; - Utils::FilePathInfo filePathInfo(const Utils::FilePath &filePath) const override; - QDateTime lastModified(const Utils::FilePath &filePath) const override; + Utils::ProcessInterface *createProcessInterface() const override; ProjectExplorer::FileTransferInterface *createFileTransferInterface( const ProjectExplorer::FileTransferSetupData &setup) const override; Utils::Environment systemEnvironment() const override; - qint64 fileSize(const Utils::FilePath &filePath) const override; - qint64 bytesAvailable(const Utils::FilePath &filePath) const override; - QFileDevice::Permissions permissions(const Utils::FilePath &filePath) const override; - bool setPermissions(const Utils::FilePath &filePath, QFileDevice::Permissions permissions) const override; protected: LinuxDevice(); diff --git a/src/tools/sdktool/CMakeLists.txt b/src/tools/sdktool/CMakeLists.txt index 050940aca96..ffd578dbcac 100644 --- a/src/tools/sdktool/CMakeLists.txt +++ b/src/tools/sdktool/CMakeLists.txt @@ -69,6 +69,7 @@ extend_qtc_library(sdktoolLib PUBLIC_DEFINES UTILS_STATIC_LIBRARY SOURCES commandline.cpp commandline.h + devicefileaccess.cpp devicefileaccess.h environment.cpp environment.h filepath.cpp filepath.h fileutils.cpp fileutils.h diff --git a/src/tools/sdktool/sdktoollib.qbs b/src/tools/sdktool/sdktoollib.qbs index 4352c1f6c06..3c27d823023 100644 --- a/src/tools/sdktool/sdktoollib.qbs +++ b/src/tools/sdktool/sdktoollib.qbs @@ -92,6 +92,7 @@ QtcLibrary { prefix: libsDir + "/utils/" files: [ "commandline.cpp", "commandline.h", + "devicefileaccess.cpp", "devicefileaccess.h", "environment.cpp", "environment.h", "filepath.cpp", "filepath.h", "fileutils.cpp", "fileutils.h", diff --git a/tests/auto/utils/fsengine/tst_fsengine.cpp b/tests/auto/utils/fsengine/tst_fsengine.cpp index 997fe26fd97..e9a21353f99 100644 --- a/tests/auto/utils/fsengine/tst_fsengine.cpp +++ b/tests/auto/utils/fsengine/tst_fsengine.cpp @@ -2,8 +2,8 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include <utils/filepath.h> -#include <utils/fileutils.h> #include <utils/fsengine/fsengine.h> +#include <utils/environment.h> #include <utils/hostosinfo.h> #include <QDebug> @@ -58,119 +58,6 @@ void tst_fsengine::initTestCase() if (HostOsInfo::isWindowsHost()) QSKIP("The fsengine tests are not supported on Windows."); - DeviceFileHooks &deviceHooks = DeviceFileHooks::instance(); - - deviceHooks.fileContents = - [](const FilePath &path, qint64 maxSize, qint64 offset) -> std::optional<QByteArray> { - return FilePath::fromString(path.path()).fileContents(maxSize, offset); - }; - - deviceHooks.isExecutableFile = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).isExecutableFile(); - }; - deviceHooks.isReadableFile = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).isReadableFile(); - }; - deviceHooks.isReadableDir = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).isReadableDir(); - }; - deviceHooks.isWritableDir = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).isWritableDir(); - }; - deviceHooks.isWritableFile = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).isWritableFile(); - }; - deviceHooks.isFile = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).isFile(); - }; - deviceHooks.isDir = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).isDir(); - }; - deviceHooks.ensureWritableDir = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).ensureWritableDir(); - }; - deviceHooks.ensureExistingFile = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).ensureExistingFile(); - }; - deviceHooks.createDir = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).createDir(); - }; - deviceHooks.exists = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).exists(); - }; - deviceHooks.removeFile = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).removeFile(); - }; - deviceHooks.removeRecursively = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).removeRecursively(); - }; - deviceHooks.copyFile = [](const FilePath &filePath, const FilePath &target) { - return FilePath::fromString(filePath.path()).copyFile(target); - }; - deviceHooks.renameFile = [](const FilePath &filePath, const FilePath &target) { - return FilePath::fromString(filePath.path()).renameFile(target); - }; - deviceHooks.searchInPath = [](const FilePath &filePath, const FilePaths &dirs) { - return FilePath::fromString(filePath.path()).searchInPath(dirs); - }; - deviceHooks.symLinkTarget = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).symLinkTarget(); - }; - deviceHooks.iterateDirectory = [](const FilePath &filePath, - const FilePath::IterateDirCallback &callBack, - const FileFilter &filter) { - const FilePath fp = FilePath::fromString(filePath.path()); - if (callBack.index() == 0) { - fp.iterateDirectory( - [&filePath, cb = std::get<0>(callBack)](const FilePath &path) { - return cb(path.onDevice(filePath)); - }, - filter); - } else { - fp.iterateDirectory( - [&filePath, cb = std::get<1>(callBack)](const FilePath &path, const FilePathInfo &fpi) { - return cb(path.onDevice(filePath), fpi); - }, - filter); - } - }; - deviceHooks.asyncFileContents = [](const Continuation<const std::optional<QByteArray> &> &cont, - const FilePath &filePath, - qint64 maxSize, - qint64 offset) { - return FilePath::fromString(filePath.path()).asyncFileContents(cont, maxSize, offset); - }; - deviceHooks.writeFileContents = [](const FilePath &filePath, - const QByteArray &data, - qint64 offset) { - return FilePath::fromString(filePath.path()).writeFileContents(data, offset); - }; - deviceHooks.lastModified = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).lastModified(); - }; - deviceHooks.permissions = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).permissions(); - }; - deviceHooks.setPermissions = [](const FilePath &filePath, QFile::Permissions permissions) { - return FilePath::fromString(filePath.path()).setPermissions(permissions); - }; - deviceHooks.osType = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).osType(); - }; - // deviceHooks.environment = [](const FilePath &filePath) -> Environment {return {};}; - deviceHooks.fileSize = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).fileSize(); - }; - deviceHooks.bytesAvailable = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).bytesAvailable(); - }; - deviceHooks.filePathInfo = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).filePathInfo(); - }; - - - deviceHooks.mapToDevicePath = [](const FilePath &filePath) { return filePath.path(); }; - FSEngine::addDevice(FilePath::fromString("device://test")); tempFolder = QDir::tempPath(); |