diff options
author | hjk <[email protected]> | 2022-10-10 17:32:56 +0200 |
---|---|---|
committer | hjk <[email protected]> | 2022-10-13 10:38:35 +0000 |
commit | 1fa32552425f0c3fd98ae20bfd26b8b85bfda3e0 (patch) | |
tree | a41b22ee65643fdb166e5ee4f4f79da658bd8f73 | |
parent | e0832ce7fca490ec1656933ce5bedb6d6da969cd (diff) |
Utils: Split off file access interface from IDevice
The file accessing functions form now a class hierarchy by themselves,
the devices return a suitable point.
The previous implementation was mildly confusing by the special handling
of the DesktopDevice, fallbacks and remote cases in the same function
leading to unnecessary boilerplate when adding new functions and
codepaths that sometimes passed the FilePath API twice.
Implemented are a "DesktopDeviceFileAccess" taking care of the
previous !needsDevice() branches and a "UnixDeviceFileAccess"
covering the current docker and RL uses.
As a side-effect this unifies to a large degree the current docker
and RL code paths with were occasionally deviating from each other
while they shouldn't.
Change-Id: I4ff59d4be2a07d13e2ca5e9ace26a84160a87c9d
Reviewed-by: Jarek Kobus <[email protected]>
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(); |